@@ -456,5 +456,76 @@ const email = "user@example.com";
456456
457457 expect ( findings ) . toHaveLength ( 0 ) ;
458458 } ) ;
459+
460+ describe ( 'charset and alphabet detection' , ( ) => {
461+ it ( 'should ignore full alphanumeric alphabet (customAlphabet pattern)' , ( ) => {
462+ // The exact case from the bug report
463+ const source = `const createBundleId = customAlphabet(
464+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
465+ 8,
466+ )` ;
467+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
468+ expect ( findings ) . toHaveLength ( 0 ) ;
469+ } ) ;
470+
471+ it ( 'should ignore lowercase-only alphabet' , ( ) => {
472+ const source = "const id = nanoid('abcdefghijklmnopqrstuvwxyz', 10);" ;
473+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
474+ expect ( findings ) . toHaveLength ( 0 ) ;
475+ } ) ;
476+
477+ it ( 'should ignore uppercase-only alphabet' , ( ) => {
478+ const source =
479+ "const code = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 6);" ;
480+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
481+ expect ( findings ) . toHaveLength ( 0 ) ;
482+ } ) ;
483+
484+ it ( 'should ignore hex charset' , ( ) => {
485+ // 16 unique chars, has a sequential run of 10 digits + 6 letters
486+ const source = "const hex = customAlphabet('0123456789abcdef', 32);" ;
487+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
488+ expect ( findings ) . toHaveLength ( 0 ) ;
489+ } ) ;
490+
491+ it ( 'should ignore base32 alphabet' , ( ) => {
492+ // RFC 4648 base32: A-Z + 2-7
493+ const source =
494+ "const encoded = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', 16);" ;
495+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
496+ expect ( findings ) . toHaveLength ( 0 ) ;
497+ } ) ;
498+
499+ it ( 'should ignore digits-only charset' , ( ) => {
500+ const source = "const pin = customAlphabet('0123456789', 6);" ;
501+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
502+ expect ( findings ) . toHaveLength ( 0 ) ;
503+ } ) ;
504+
505+ it ( 'should still detect a real high-entropy secret that is not a charset' , ( ) => {
506+ // Looks like a real token — no sequential runs, no large unique set structure
507+ const source =
508+ 'const token = "xK9mQwP2zLsR8tYu5nV7cJ4hFgD6eS1iO0pA3bC";' ;
509+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
510+ // Should still be flagged as entropy finding
511+ expect ( findings . length ) . toBeGreaterThan ( 0 ) ;
512+ expect ( findings . some ( ( f ) => f . kind === 'entropy' ) ) . toBe ( true ) ;
513+ } ) ;
514+
515+ it ( 'should still detect AWS key even if it superficially resembles an alphabet' , ( ) => {
516+ const source = 'const key = "AKIAIOSFODNN7EXAMPLE";' ;
517+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
518+ expect ( findings ) . toHaveLength ( 1 ) ;
519+ expect ( findings [ 0 ] . severity ) . toBe ( 'high' ) ;
520+ } ) ;
521+
522+ it ( 'should ignore alphabet assigned to a variable without a function call' , ( ) => {
523+ // Charset used as a plain constant, not inside a function
524+ const source =
525+ "const ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';" ;
526+ const findings = detectSecretsInSource ( 'test.ts' , source ) ;
527+ expect ( findings ) . toHaveLength ( 0 ) ;
528+ } ) ;
529+ } ) ;
459530 } ) ;
460531} ) ;
0 commit comments