File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -29,7 +29,7 @@ export default tseslint.config(
2929 selector : 'JSXAttribute[name.name="dangerouslySetInnerHTML"]' ,
3030 message :
3131 'dangerouslySetInnerHTML is banned -- use text content or a sanitization library. ' +
32- 'If absolutely necessary, add // eslint-disable-next-line with a justification comment.' ,
32+ 'If absolutely necessary, add // eslint-disable-next-line no-restricted-syntax with a justification comment.' ,
3333 } ,
3434 ] ,
3535 // Rule flags every obj[var] with no data-flow analysis -- too many false
Original file line number Diff line number Diff line change 55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 " />
66 < meta name ="description " content ="SynthOrg -- monitoring and management dashboard for synthetic organizations " />
77 <!-- CSP nonce: nginx sub_filter replaces __CSP_NONCE__ on each request.
8- Uncomment when CSP is tightened to use nonce-based style-src. -->
8+ Uncomment when style-src 'unsafe-inline' in security-headers.conf is
9+ replaced with nonce-based policy. See App.tsx MotionConfig + lib/csp.ts. -->
910 <!-- <meta name="csp-nonce" content="__CSP_NONCE__" /> -->
1011 < link rel ="icon " type ="image/svg+xml " href ="/favicon.svg " />
1112 < title > SynthOrg Dashboard</ title >
Original file line number Diff line number Diff line change @@ -42,4 +42,31 @@ describe('getCspNonce', () => {
4242 const { getCspNonce } = await import ( '@/lib/csp' )
4343 expect ( getCspNonce ( ) ) . toBeUndefined ( )
4444 } )
45+
46+ it ( 'returns undefined for whitespace-only content' , async ( ) => {
47+ const meta = document . createElement ( 'meta' )
48+ meta . name = 'csp-nonce'
49+ meta . content = ' '
50+ document . head . appendChild ( meta )
51+
52+ const { getCspNonce } = await import ( '@/lib/csp' )
53+ expect ( getCspNonce ( ) ) . toBeUndefined ( )
54+ } )
55+
56+ it ( 'caches absent result and does not re-query DOM' , async ( ) => {
57+ const spy = vi . spyOn ( document , 'querySelector' )
58+
59+ const { getCspNonce } = await import ( '@/lib/csp' )
60+ expect ( getCspNonce ( ) ) . toBeUndefined ( )
61+ expect ( getCspNonce ( ) ) . toBeUndefined ( )
62+
63+ // querySelector called once during import-time init + once for first call
64+ // Second call should hit cache, not query DOM again
65+ const cspCalls = spy . mock . calls . filter (
66+ ( [ sel ] ) => sel === 'meta[name="csp-nonce"]' ,
67+ )
68+ expect ( cspCalls ) . toHaveLength ( 1 )
69+
70+ spy . mockRestore ( )
71+ } )
4572} )
Original file line number Diff line number Diff line change 55 * infrastructure (e.g. nginx `sub_filter`). If no meta tag is present
66 * (local dev, environments without CSP nonce injection), returns `undefined`.
77 *
8- * The value is read once and cached for the lifetime of the page.
8+ * The value is read once on first call and cached for the lifetime of the
9+ * page -- both present and absent results are cached.
910 */
1011
11- let cached : string | undefined
12+ const UNREAD : unique symbol = Symbol ( 'unread' )
13+ let cached : string | undefined | typeof UNREAD = UNREAD
1214
1315export function getCspNonce ( ) : string | undefined {
14- if ( cached !== undefined ) return cached
16+ if ( cached !== UNREAD ) return cached as string | undefined
1517
1618 const meta = document . querySelector < HTMLMetaElement > (
1719 'meta[name="csp-nonce"]' ,
1820 )
19- cached = meta ?. content || undefined
21+ cached = meta ?. content ?. trim ( ) || undefined
2022 return cached
2123}
You can’t perform that action at this time.
0 commit comments