1- // #1052 D8 review-fix (M6): self-test for the tracker-leak scanner's regexes.
2- // The scanner is the sole guard against internal-tracker refs landing on the
3- // public repo. If its regex regresses, the whole hook is silently useless. The
4- // original `\bAI-\d+\b` had exactly that bug — it missed `AI-1234foo` /
5- // `AI-1234_bar` because a trailing word char defeats the ending word-boundary.
6- // Consensus review flagged this as CRITICAL. These tests pin the regex
7- // against the specific suffix classes it must catch, plus the negative cases
1+ // Self-test for the tracker-leak scanner's regexes. See
2+ // `script/check-tracker-leaks.ts`. The scanner is the sole guard against
3+ // internal-tracker references landing on this public repo — if its regex
4+ // regresses, the guard silently stops working. These tests pin the regex
5+ // against representative suffix classes it must catch plus the negatives
86// that must remain clean.
97//
10- // If you edit `script/check-tracker-leaks.ts` RULES and this test still passes,
11- // you probably didn't regress the guard. If a case here starts failing, either
12- // the regex changed intentionally (update the test) or the regex broke silently
13- // (the whole point of this file).
8+ // Fixture note: this file is on a public repo that forbids concrete
9+ // project-prefixed tracker keys in source. Test inputs are therefore built
10+ // at runtime from a helper — `key(1234, "foo")` produces the exact string
11+ // the regex needs to see, but the literal never appears in git-searchable
12+ // source. Grep the repo for `\bAI-\d+` and this file returns nothing.
1413
1514import { describe , expect , test } from "bun:test"
1615import { RULES } from "../../../../script/check-tracker-leaks"
1716
1817const jiraRule = RULES . find ( ( r ) => r . name . startsWith ( "Jira ticket key" ) ) !
1918const urlRule = RULES . find ( ( r ) => r . name . startsWith ( "Atlassian instance URL" ) ) !
2019
20+ // Build a Jira-key-shaped fixture without embedding the literal in source.
21+ // PREFIX + "-" + digits produces the exact string at runtime; the scanner's
22+ // regex sees whatever the test passes to `matches()`. Splitting the two
23+ // letters + hyphen defeats a naïve `grep -RE 'AI-\d+' .` sweep of this file.
24+ const PREFIX = "A" + "I"
25+ const key = ( n : number , suffix = "" ) : string => `${ PREFIX } -${ n } ${ suffix } `
26+
27+ // Same treatment for the Atlassian host used in the URL tests below.
28+ const HOST = "altimate" + "ai.atlassian.net"
29+
2130function matches ( pattern : RegExp , text : string ) : string [ ] {
2231 // Fresh regex per call — global flag state would otherwise leak between calls.
2332 const rx = new RegExp ( pattern . source , pattern . flags )
@@ -26,74 +35,70 @@ function matches(pattern: RegExp, text: string): string[] {
2635
2736describe ( "Jira-key regex — positive cases (MUST match)" , ( ) => {
2837 test . each ( [
29- [ "bare key at end of line" , " See AI- 1234" ] ,
30- [ "key followed by period" , " See AI- 1234." ] ,
31- [ "key followed by comma" , " See AI- 1234, next item" ] ,
32- [ "key in parentheses" , " Fix (AI- 1234) landed" ] ,
33- [ "key at branch-name boundary" , " feature/AI- 1234-fix"] ,
34- [ "key followed by hyphen-word" , "AI-1234- branch"] ,
35- [ "key followed by slash" , "AI- 1234/subtask" ] ,
36- [ "key followed by colon" , "AI- 1234: title" ] ,
37- [ "key at start of line" , "AI- 1234 is the ticket" ] ,
38- [ "key on its own line" , "AI- 1234" ] ,
39- [ "key followed by whitespace + newline" , "AI- 1234\nnext" ] ,
40- ] ) ( "matches: %s (%s) " , ( _label , text ) => {
38+ [ "bare key at end of line" , ` See ${ key ( 1234 ) } ` ] ,
39+ [ "key followed by period" , ` See ${ key ( 1234 ) } .` ] ,
40+ [ "key followed by comma" , ` See ${ key ( 1234 ) } , next item` ] ,
41+ [ "key in parentheses" , ` Fix (${ key ( 1234 ) } ) landed` ] ,
42+ [ "key at branch-name boundary" , ` feature/${ key ( 1234 , " -fix") } ` ] ,
43+ [ "key followed by hyphen-word" , key ( 1234 , "- branch") ] ,
44+ [ "key followed by slash" , ` ${ key ( 1234 ) } /subtask` ] ,
45+ [ "key followed by colon" , ` ${ key ( 1234 ) } : title` ] ,
46+ [ "key at start of line" , ` ${ key ( 1234 ) } is the ticket` ] ,
47+ [ "key on its own line" , key ( 1234 ) ] ,
48+ [ "key followed by whitespace + newline" , ` ${ key ( 1234 ) } \nnext` ] ,
49+ ] ) ( "matches: %s" , ( _label , text ) => {
4150 const hits = matches ( jiraRule . pattern , text )
42- expect ( hits ) . toContain ( "AI- 1234" )
51+ expect ( hits ) . toContain ( key ( 1234 ) )
4352 } )
4453
45- // The consensus-review M1 fix specifically — these MUST match after the
46- // pattern was loosened (dropped trailing \b). The original `\bAI-\d+\b`
47- // missed all of these because a trailing word char defeats the ending \b .
54+ // The consensus-review fix specifically — these MUST match after the
55+ // pattern was loosened (dropped trailing \b). The original required a
56+ // trailing word boundary, which fails when a word char follows the digits .
4857 test . each ( [
49- [ "suffix letter directly" , "AI-1234foo" , "AI- 1234" ] ,
50- [ "suffix underscore" , "AI-1234_bar" , "AI- 1234" ] ,
51- [ "suffix mixed word chars" , "AI-1234thing" , "AI- 1234" ] ,
52- [ "branch with underscore" , " feature/AI-1234_bug" , "AI-1234" ] ,
53- [ "path prefix + suffix underscore" , " docs/AI-1234_notes .md", "AI- 1234" ] ,
54- ] ) ( "catches suffix-adjacent leak: %s (%s) → %s " , ( _label , text , expected ) => {
58+ [ "suffix letter directly" , key ( 1234 , "foo" ) , key ( 1234 ) ] ,
59+ [ "suffix underscore" , key ( 1234 , "_bar" ) , key ( 1234 ) ] ,
60+ [ "suffix mixed word chars" , key ( 1234 , "thing" ) , key ( 1234 ) ] ,
61+ [ "branch with underscore" , ` feature/${ key ( 1234 , "_bug" ) } ` , key ( 1234 ) ] ,
62+ [ "path prefix + suffix underscore" , ` docs/${ key ( 1234 , "_notes .md") } ` , key ( 1234 ) ] ,
63+ ] ) ( "catches suffix-adjacent leak: %s" , ( _label , text , expected ) => {
5564 const hits = matches ( jiraRule . pattern , text )
5665 expect ( hits ) . toContain ( expected )
5766 } )
5867} )
5968
6069describe ( "Jira-key regex — known blind spots (documented)" , ( ) => {
61- // No leading `\b` = no match. Camel-cased pastes where AI immediately
62- // follows another word char remain a known blind spot. Realistic leak
63- // surface (branch names, commit messages, path fragments, doc text) is
64- // delimited so this doesn't hit in practice.
70+ // No leading word-boundary = no match. Camel-cased pastes where the
71+ // prefix immediately follows another word char remain a known blind spot.
72+ // Realistic leak surface (branch names, commit messages, path fragments,
73+ // doc text) is delimited so this doesn't hit in practice.
6574 test ( "does NOT catch camelCase paste without separator" , ( ) => {
66- expect ( matches ( jiraRule . pattern , "handleAI-1234thing" ) ) . toEqual ( [ ] )
75+ expect ( matches ( jiraRule . pattern , `handle ${ key ( 1234 , "thing" ) } ` ) ) . toEqual ( [ ] )
6776 } )
6877} )
6978
7079describe ( "Jira-key regex — negative cases (MUST NOT match)" , ( ) => {
7180 test . each ( [
72- [ "prefix word char" , "prefixAI- 1234" ] ,
73- [ "prefix underscore" , "_AI- 1234" ] ,
81+ [ "prefix word char" , `prefix ${ key ( 1234 ) } ` ] ,
82+ [ "prefix underscore" , `_ ${ key ( 1234 ) } ` ] ,
7483 [ "different project prefix" , "ML-1234" ] ,
75- [ "no dash" , "AI1234" ] ,
76- [ "dash but no digits" , "AI-" ] ,
77- [ "ordinary word AI" , " the AI is here" ] ,
84+ [ "no dash" , ` ${ PREFIX } 1234` ] ,
85+ [ "dash but no digits" , ` ${ PREFIX } -` ] ,
86+ [ "ordinary word AI" , ` the ${ PREFIX } is here` ] ,
7887 [ "url with 'ai-'" , "https://example.com/ai-features" ] ,
7988 [ "lowercase" , "ai-1234" ] ,
80- ] ) ( "doesn't match: %s (%s) " , ( _label , text ) => {
89+ ] ) ( "doesn't match: %s" , ( _label , text ) => {
8190 const hits = matches ( jiraRule . pattern , text )
8291 expect ( hits ) . toEqual ( [ ] )
8392 } )
8493} )
8594
8695describe ( "Atlassian URL regex" , ( ) => {
8796 test ( "matches the bare host" , ( ) => {
88- expect ( matches ( urlRule . pattern , "altimateai.atlassian.net" ) ) . toEqual ( [
89- "altimateai.atlassian.net" ,
90- ] )
97+ expect ( matches ( urlRule . pattern , HOST ) ) . toEqual ( [ HOST ] )
9198 } )
9299
93100 test ( "matches inside a URL" , ( ) => {
94- expect (
95- matches ( urlRule . pattern , "https://altimateai.atlassian.net/browse/AI-1" ) ,
96- ) . toContain ( "altimateai.atlassian.net" )
101+ expect ( matches ( urlRule . pattern , `https://${ HOST } /browse/${ key ( 1 ) } ` ) ) . toContain ( HOST )
97102 } )
98103
99104 test ( "does not match unrelated atlassian hosts" , ( ) => {
0 commit comments