@mmnto/totem 1.8.3 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/compile-lesson.d.ts +2 -0
  2. package/dist/compile-lesson.d.ts.map +1 -1
  3. package/dist/compile-lesson.js +58 -2
  4. package/dist/compile-lesson.js.map +1 -1
  5. package/dist/compile-lesson.test.js +143 -0
  6. package/dist/compile-lesson.test.js.map +1 -1
  7. package/dist/eslint-adapter.d.ts +18 -0
  8. package/dist/eslint-adapter.d.ts.map +1 -0
  9. package/dist/eslint-adapter.js +173 -0
  10. package/dist/eslint-adapter.js.map +1 -0
  11. package/dist/eslint-adapter.test.d.ts +2 -0
  12. package/dist/eslint-adapter.test.d.ts.map +1 -0
  13. package/dist/eslint-adapter.test.js +110 -0
  14. package/dist/eslint-adapter.test.js.map +1 -0
  15. package/dist/index.d.ts +9 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +6 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/lesson-linter.d.ts.map +1 -1
  20. package/dist/lesson-linter.js +50 -1
  21. package/dist/lesson-linter.js.map +1 -1
  22. package/dist/lesson-linter.test.js +63 -0
  23. package/dist/lesson-linter.test.js.map +1 -1
  24. package/dist/lesson-pattern.d.ts +9 -0
  25. package/dist/lesson-pattern.d.ts.map +1 -1
  26. package/dist/lesson-pattern.js +30 -0
  27. package/dist/lesson-pattern.js.map +1 -1
  28. package/dist/lesson-pattern.test.js +91 -1
  29. package/dist/lesson-pattern.test.js.map +1 -1
  30. package/dist/pipeline-observation.d.ts +36 -0
  31. package/dist/pipeline-observation.d.ts.map +1 -0
  32. package/dist/pipeline-observation.js +100 -0
  33. package/dist/pipeline-observation.js.map +1 -0
  34. package/dist/pipeline-observation.test.d.ts +2 -0
  35. package/dist/pipeline-observation.test.d.ts.map +1 -0
  36. package/dist/pipeline-observation.test.js +172 -0
  37. package/dist/pipeline-observation.test.js.map +1 -0
  38. package/dist/regex-utils.d.ts +24 -0
  39. package/dist/regex-utils.d.ts.map +1 -0
  40. package/dist/regex-utils.js +39 -0
  41. package/dist/regex-utils.js.map +1 -0
  42. package/dist/regex-utils.test.d.ts +2 -0
  43. package/dist/regex-utils.test.d.ts.map +1 -0
  44. package/dist/regex-utils.test.js +73 -0
  45. package/dist/regex-utils.test.js.map +1 -0
  46. package/dist/semgrep-adapter.d.ts +15 -0
  47. package/dist/semgrep-adapter.d.ts.map +1 -0
  48. package/dist/semgrep-adapter.js +133 -0
  49. package/dist/semgrep-adapter.js.map +1 -0
  50. package/dist/semgrep-adapter.test.d.ts +2 -0
  51. package/dist/semgrep-adapter.test.d.ts.map +1 -0
  52. package/dist/semgrep-adapter.test.js +136 -0
  53. package/dist/semgrep-adapter.test.js.map +1 -0
  54. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { extractAllFields, extractManualPattern, extractRuleExamples, stripInlineCode, } from './lesson-pattern.js';
2
+ import { extractAllFields, extractBadGoodSnippets, extractManualPattern, extractRuleExamples, stripInlineCode, } from './lesson-pattern.js';
3
3
  describe('extractManualPattern', () => {
4
4
  it('extracts all fields from a well-formed lesson body', () => {
5
5
  const body = [
@@ -103,4 +103,94 @@ describe('extractRuleExamples', () => {
103
103
  expect(result.misses).toEqual([]);
104
104
  });
105
105
  });
106
+ describe('extractBadGoodSnippets', () => {
107
+ it('extracts fenced code blocks', () => {
108
+ const body = [
109
+ '**Bad:**',
110
+ '```ts',
111
+ 'console.log("bad");',
112
+ '```',
113
+ '',
114
+ '**Good:**',
115
+ '```ts',
116
+ 'logger.info("good");',
117
+ '```',
118
+ ].join('\n');
119
+ const result = extractBadGoodSnippets(body);
120
+ expect(result).not.toBeNull();
121
+ expect(result.bad).toEqual(['console.log("bad");']);
122
+ expect(result.good).toEqual(['logger.info("good");']);
123
+ });
124
+ it('extracts inline single-line snippets', () => {
125
+ const body = '**Bad:** `console.log("bad")`\n**Good:** `logger.info("good")`';
126
+ const result = extractBadGoodSnippets(body);
127
+ expect(result).not.toBeNull();
128
+ expect(result.bad).toEqual(['console.log("bad")']);
129
+ expect(result.good).toEqual(['logger.info("good")']);
130
+ });
131
+ it('returns null when no Bad/Good fields', () => {
132
+ const body = 'Just a normal lesson body without pattern fields.';
133
+ expect(extractBadGoodSnippets(body)).toBeNull();
134
+ });
135
+ it('returns null when only Bad (no Good)', () => {
136
+ const body = '**Bad:** `console.log("bad")`';
137
+ expect(extractBadGoodSnippets(body)).toBeNull();
138
+ });
139
+ it('returns null when only Good (no Bad)', () => {
140
+ const body = '**Good:** `logger.info("good")`';
141
+ expect(extractBadGoodSnippets(body)).toBeNull();
142
+ });
143
+ it('handles mixed formats (fenced Bad, inline Good)', () => {
144
+ const body = [
145
+ '**Bad:**',
146
+ '```ts',
147
+ 'console.log("bad");',
148
+ '```',
149
+ '**Good:** `logger.info("good")`',
150
+ ].join('\n');
151
+ const result = extractBadGoodSnippets(body);
152
+ expect(result).not.toBeNull();
153
+ expect(result.bad).toEqual(['console.log("bad");']);
154
+ expect(result.good).toEqual(['logger.info("good")']);
155
+ });
156
+ it('filters empty lines from fenced snippets', () => {
157
+ const body = [
158
+ '**Bad:**',
159
+ '```ts',
160
+ '',
161
+ 'console.log("bad");',
162
+ '',
163
+ '```',
164
+ '**Good:**',
165
+ '```ts',
166
+ '',
167
+ 'logger.info("good");',
168
+ '',
169
+ '```',
170
+ ].join('\n');
171
+ const result = extractBadGoodSnippets(body);
172
+ expect(result).not.toBeNull();
173
+ expect(result.bad).toEqual(['console.log("bad");']);
174
+ expect(result.good).toEqual(['logger.info("good");']);
175
+ });
176
+ it('extracts multi-line fenced code blocks', () => {
177
+ const body = [
178
+ '**Bad:**',
179
+ '```ts',
180
+ 'const x = 1;',
181
+ 'console.log(x);',
182
+ '```',
183
+ '',
184
+ '**Good:**',
185
+ '```ts',
186
+ 'const x = 1;',
187
+ 'logger.info(x);',
188
+ '```',
189
+ ].join('\n');
190
+ const result = extractBadGoodSnippets(body);
191
+ expect(result).not.toBeNull();
192
+ expect(result.bad).toEqual(['const x = 1;', 'console.log(x);']);
193
+ expect(result.good).toEqual(['const x = 1;', 'logger.info(x);']);
194
+ });
195
+ });
106
196
  //# sourceMappingURL=lesson-pattern.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lesson-pattern.test.js","sourceRoot":"","sources":["../src/lesson-pattern.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lesson-pattern.test.js","sourceRoot":"","sources":["../src/lesson-pattern.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pipeline 5 — Observation-based auto-capture.
3
+ *
4
+ * Converts shield findings into lint rules by extracting the offending
5
+ * source line, converting it to a regex pattern, and wrapping it in a
6
+ * CompiledRule. A deduplication pass merges rules with identical patterns.
7
+ */
8
+ import type { CompiledRule } from './compiler-schema.js';
9
+ /** Describes a single shield observation that should be turned into a rule. */
10
+ export interface ObservationInput {
11
+ /** File path where the finding was reported. */
12
+ file: string;
13
+ /** 1-based line number of the finding. */
14
+ line: number;
15
+ /** Shield finding message. */
16
+ message: string;
17
+ /** Full content of the file. */
18
+ fileContent: string;
19
+ }
20
+ /**
21
+ * Generate a CompiledRule from a shield observation.
22
+ *
23
+ * Returns `null` when the target line is out of range, empty, or produces
24
+ * an empty pattern.
25
+ *
26
+ * **Invariant (ADR-058):** severity is always `'warning'`, never `'error'`.
27
+ */
28
+ export declare function generateObservationRule(input: ObservationInput): CompiledRule | null;
29
+ /**
30
+ * Merge rules that share the same `lessonHash` (i.e., identical pattern).
31
+ *
32
+ * For duplicates the unique messages are joined with ` | ` and
33
+ * fileGlobs are merged so the rule covers all observed file types.
34
+ */
35
+ export declare function deduplicateObservations(rules: CompiledRule[]): CompiledRule[];
36
+ //# sourceMappingURL=pipeline-observation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-observation.d.ts","sourceRoot":"","sources":["../src/pipeline-observation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAKzD,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;CACrB;AAID;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,GAAG,IAAI,CAuCpF;AAID;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAwC7E"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Pipeline 5 — Observation-based auto-capture.
3
+ *
4
+ * Converts shield findings into lint rules by extracting the offending
5
+ * source line, converting it to a regex pattern, and wrapping it in a
6
+ * CompiledRule. A deduplication pass merges rules with identical patterns.
7
+ */
8
+ import { createHash } from 'node:crypto';
9
+ import { extname } from 'node:path';
10
+ import { codeToPattern } from './regex-utils.js';
11
+ // ─── Generator ─────────────────────────────────────
12
+ /**
13
+ * Generate a CompiledRule from a shield observation.
14
+ *
15
+ * Returns `null` when the target line is out of range, empty, or produces
16
+ * an empty pattern.
17
+ *
18
+ * **Invariant (ADR-058):** severity is always `'warning'`, never `'error'`.
19
+ */
20
+ export function generateObservationRule(input) {
21
+ const lines = input.fileContent.split('\n');
22
+ // 1-based → 0-based
23
+ const idx = input.line - 1;
24
+ if (idx < 0 || idx >= lines.length) {
25
+ return null;
26
+ }
27
+ const sourceLine = lines[idx];
28
+ // Reject empty / whitespace-only lines
29
+ if (!sourceLine || sourceLine.trim().length === 0) {
30
+ return null;
31
+ }
32
+ const pattern = codeToPattern(sourceLine);
33
+ if (pattern === '') {
34
+ return null;
35
+ }
36
+ const hash = createHash('sha256').update(`pipeline-5:${pattern}`).digest('hex').slice(0, 16);
37
+ const ext = extname(input.file); // e.g. ".ts"
38
+ const fileGlobs = ext ? [`**/*${ext}`] : undefined;
39
+ const now = new Date().toISOString();
40
+ return {
41
+ lessonHash: hash,
42
+ lessonHeading: 'Pipeline 5: observation from shield',
43
+ pattern,
44
+ message: input.message,
45
+ engine: 'regex',
46
+ severity: 'warning',
47
+ fileGlobs,
48
+ compiledAt: now,
49
+ createdAt: now,
50
+ };
51
+ }
52
+ // ─── Deduplicator ──────────────────────────────────
53
+ /**
54
+ * Merge rules that share the same `lessonHash` (i.e., identical pattern).
55
+ *
56
+ * For duplicates the unique messages are joined with ` | ` and
57
+ * fileGlobs are merged so the rule covers all observed file types.
58
+ */
59
+ export function deduplicateObservations(rules) {
60
+ const map = new Map();
61
+ const messageMap = new Map();
62
+ const globMap = new Map();
63
+ for (const rule of rules) {
64
+ const hash = rule.lessonHash;
65
+ const existing = map.get(hash);
66
+ if (!existing) {
67
+ map.set(hash, { ...rule });
68
+ messageMap.set(hash, [rule.message]);
69
+ globMap.set(hash, rule.fileGlobs ? new Set(rule.fileGlobs) : undefined);
70
+ }
71
+ else {
72
+ const messages = messageMap.get(hash);
73
+ if (!messages.includes(rule.message)) {
74
+ messages.push(rule.message);
75
+ }
76
+ const currentGlobs = globMap.get(hash);
77
+ if (currentGlobs !== undefined) {
78
+ if (rule.fileGlobs === undefined) {
79
+ globMap.set(hash, undefined); // No globs = match everything
80
+ }
81
+ else {
82
+ for (const g of rule.fileGlobs)
83
+ currentGlobs.add(g);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ const result = [];
89
+ for (const [hash, rule] of map) {
90
+ const messages = messageMap.get(hash);
91
+ const globs = globMap.get(hash);
92
+ result.push({
93
+ ...rule,
94
+ message: messages.join(' | '),
95
+ fileGlobs: globs ? [...globs].sort() : undefined,
96
+ });
97
+ }
98
+ return result;
99
+ }
100
+ //# sourceMappingURL=pipeline-observation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-observation.js","sourceRoot":"","sources":["../src/pipeline-observation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pipeline-observation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-observation.test.d.ts","sourceRoot":"","sources":["../src/pipeline-observation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,172 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { deduplicateObservations, generateObservationRule, } from './pipeline-observation.js';
3
+ const FIVE_LINE_FILE = [
4
+ 'import { foo } from "bar";',
5
+ '',
6
+ 'const result = foo(42);',
7
+ 'console.log(result);',
8
+ 'export default result;',
9
+ ].join('\n');
10
+ describe('generateObservationRule', () => {
11
+ it('generates warning severity rule from extracted file content', () => {
12
+ const input = {
13
+ file: 'src/example.ts',
14
+ line: 3,
15
+ message: 'Avoid magic numbers',
16
+ fileContent: FIVE_LINE_FILE,
17
+ };
18
+ const rule = generateObservationRule(input);
19
+ expect(rule).not.toBeNull();
20
+ expect(rule.severity).toBe('warning');
21
+ expect(rule.engine).toBe('regex');
22
+ expect(rule.pattern).toBeTruthy();
23
+ // Pattern should match the original line
24
+ const re = new RegExp(rule.pattern);
25
+ expect(re.test('const result = foo(42);')).toBe(true);
26
+ });
27
+ it('returns null for out-of-range line number', () => {
28
+ const input = {
29
+ file: 'src/example.ts',
30
+ line: 100,
31
+ message: 'Some finding',
32
+ fileContent: FIVE_LINE_FILE,
33
+ };
34
+ expect(generateObservationRule(input)).toBeNull();
35
+ });
36
+ it('returns null for empty/whitespace-only line', () => {
37
+ const input = {
38
+ file: 'src/example.ts',
39
+ line: 2, // The empty line in FIVE_LINE_FILE
40
+ message: 'Some finding',
41
+ fileContent: FIVE_LINE_FILE,
42
+ };
43
+ expect(generateObservationRule(input)).toBeNull();
44
+ });
45
+ it('derives fileGlobs from file extension', () => {
46
+ const cases = [
47
+ { file: 'src/app.ts', expected: '**/*.ts' },
48
+ { file: 'lib/utils.py', expected: '**/*.py' },
49
+ { file: 'cmd/main.go', expected: '**/*.go' },
50
+ { file: 'src/App.jsx', expected: '**/*.jsx' },
51
+ ];
52
+ for (const { file, expected } of cases) {
53
+ const input = {
54
+ file,
55
+ line: 1,
56
+ message: 'test',
57
+ fileContent: 'import { foo } from "bar";',
58
+ };
59
+ const rule = generateObservationRule(input);
60
+ expect(rule).not.toBeNull();
61
+ expect(rule.fileGlobs).toEqual([expected]);
62
+ }
63
+ });
64
+ it('computes deterministic lessonHash from pattern', () => {
65
+ const inputA = {
66
+ file: 'src/a.ts',
67
+ line: 1,
68
+ message: 'msg1',
69
+ fileContent: 'const x = 1;',
70
+ };
71
+ const inputB = {
72
+ file: 'src/b.ts',
73
+ line: 1,
74
+ message: 'msg2',
75
+ fileContent: 'const x = 1;', // Same content → same pattern → same hash
76
+ };
77
+ const inputC = {
78
+ file: 'src/c.ts',
79
+ line: 1,
80
+ message: 'msg3',
81
+ fileContent: 'const y = 2;', // Different content → different hash
82
+ };
83
+ const ruleA = generateObservationRule(inputA);
84
+ const ruleB = generateObservationRule(inputB);
85
+ const ruleC = generateObservationRule(inputC);
86
+ // Same pattern → same hash
87
+ expect(ruleA.lessonHash).toBe(ruleB.lessonHash);
88
+ // Different pattern → different hash
89
+ expect(ruleA.lessonHash).not.toBe(ruleC.lessonHash);
90
+ // Hash is 16 hex chars
91
+ expect(ruleA.lessonHash).toMatch(/^[0-9a-f]{16}$/);
92
+ });
93
+ it('generated pattern matches the original line', () => {
94
+ const input = {
95
+ file: 'src/example.ts',
96
+ line: 4,
97
+ message: 'Avoid console.log',
98
+ fileContent: FIVE_LINE_FILE,
99
+ };
100
+ const rule = generateObservationRule(input);
101
+ const re = new RegExp(rule.pattern);
102
+ expect(re.test('console.log(result);')).toBe(true);
103
+ });
104
+ it('severity is always warning, never error', () => {
105
+ const input = {
106
+ file: 'src/example.ts',
107
+ line: 1,
108
+ message: 'CRITICAL: security vulnerability',
109
+ fileContent: 'eval(userInput);',
110
+ };
111
+ const rule = generateObservationRule(input);
112
+ // Even for critical findings, observation rules are always warnings (ADR-058)
113
+ expect(rule.severity).toBe('warning');
114
+ expect(rule.severity).not.toBe('error');
115
+ });
116
+ });
117
+ describe('deduplicateObservations', () => {
118
+ it('deduplicates rules with identical patterns', () => {
119
+ const base = {
120
+ file: 'src/a.ts',
121
+ line: 1,
122
+ message: '',
123
+ fileContent: 'const x = 1;',
124
+ };
125
+ const rule1 = generateObservationRule({ ...base, message: 'Finding A' });
126
+ const rule2 = generateObservationRule({ ...base, message: 'Finding B' });
127
+ const rule3 = generateObservationRule({ ...base, message: 'Finding C' });
128
+ const result = deduplicateObservations([rule1, rule2, rule3]);
129
+ expect(result).toHaveLength(1);
130
+ // All unique messages should be merged
131
+ expect(result[0].message).toContain('Finding A');
132
+ expect(result[0].message).toContain('Finding B');
133
+ expect(result[0].message).toContain('Finding C');
134
+ expect(result[0].message).toBe('Finding A | Finding B | Finding C');
135
+ });
136
+ it('preserves distinct rules', () => {
137
+ const ruleA = generateObservationRule({
138
+ file: 'src/a.ts',
139
+ line: 1,
140
+ message: 'Finding A',
141
+ fileContent: 'const x = 1;',
142
+ });
143
+ const ruleB = generateObservationRule({
144
+ file: 'src/b.ts',
145
+ line: 1,
146
+ message: 'Finding B',
147
+ fileContent: 'const y = 2;',
148
+ });
149
+ const result = deduplicateObservations([ruleA, ruleB]);
150
+ expect(result).toHaveLength(2);
151
+ });
152
+ it('merges fileGlobs from different extensions', () => {
153
+ const ruleTs = generateObservationRule({
154
+ file: 'src/a.ts',
155
+ line: 1,
156
+ message: 'Finding in TS',
157
+ fileContent: 'const x = 1;',
158
+ });
159
+ const ruleJs = generateObservationRule({
160
+ file: 'src/b.js',
161
+ line: 1,
162
+ message: 'Finding in JS',
163
+ fileContent: 'const x = 1;',
164
+ });
165
+ const result = deduplicateObservations([ruleTs, ruleJs]);
166
+ expect(result).toHaveLength(1);
167
+ expect(result[0].fileGlobs).toEqual(['**/*.js', '**/*.ts']);
168
+ expect(result[0].message).toContain('Finding in TS');
169
+ expect(result[0].message).toContain('Finding in JS');
170
+ });
171
+ });
172
+ //# sourceMappingURL=pipeline-observation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-observation.test.js","sourceRoot":"","sources":["../src/pipeline-observation.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Centralized regex utilities for safe pattern construction.
3
+ *
4
+ * Replaces the scattered `string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')`
5
+ * pattern found across the codebase with a single, tested function.
6
+ */
7
+ /**
8
+ * Escape all regex special characters in a string so it can be used
9
+ * as a literal match inside a `RegExp`.
10
+ */
11
+ export declare function escapeRegex(s: string): string;
12
+ /**
13
+ * Convert a literal code snippet into a permissive regex pattern for
14
+ * Pipeline 5 observation rules.
15
+ *
16
+ * Steps:
17
+ * 1. Trim leading/trailing blank lines
18
+ * 2. Escape regex metacharacters
19
+ * 3. Replace whitespace runs with `\s+` to tolerate reformatting
20
+ *
21
+ * Returns `''` for empty / whitespace-only input.
22
+ */
23
+ export declare function codeToPattern(code: string): string;
24
+ //# sourceMappingURL=regex-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regex-utils.d.ts","sourceRoot":"","sources":["../src/regex-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBlD"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Centralized regex utilities for safe pattern construction.
3
+ *
4
+ * Replaces the scattered `string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')`
5
+ * pattern found across the codebase with a single, tested function.
6
+ */
7
+ /**
8
+ * Escape all regex special characters in a string so it can be used
9
+ * as a literal match inside a `RegExp`.
10
+ */
11
+ export function escapeRegex(s) {
12
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13
+ }
14
+ /**
15
+ * Convert a literal code snippet into a permissive regex pattern for
16
+ * Pipeline 5 observation rules.
17
+ *
18
+ * Steps:
19
+ * 1. Trim leading/trailing blank lines
20
+ * 2. Escape regex metacharacters
21
+ * 3. Replace whitespace runs with `\s+` to tolerate reformatting
22
+ *
23
+ * Returns `''` for empty / whitespace-only input.
24
+ */
25
+ export function codeToPattern(code) {
26
+ // Strip leading and trailing blank lines
27
+ const trimmed = code
28
+ .replace(/^[\t ]*\n/, '')
29
+ .replace(/\n[\t ]*$/, '')
30
+ .trim();
31
+ if (trimmed.length === 0) {
32
+ return '';
33
+ }
34
+ const escaped = escapeRegex(trimmed);
35
+ // Replace any run of whitespace (including escaped newlines) with \s+
36
+ const pattern = escaped.replace(/\s+/g, '\\s+');
37
+ return pattern;
38
+ }
39
+ //# sourceMappingURL=regex-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regex-utils.js","sourceRoot":"","sources":["../src/regex-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,yCAAyC;IACzC,MAAM,OAAO,GAAG,IAAI;SACjB,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,IAAI,EAAE,CAAC;IAEV,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAErC,sEAAsE;IACtE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=regex-utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regex-utils.test.d.ts","sourceRoot":"","sources":["../src/regex-utils.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { codeToPattern, escapeRegex } from './regex-utils.js';
3
+ describe('escapeRegex', () => {
4
+ it('escapes all regex metacharacters', () => {
5
+ const metacharacters = '.*+?^${}()|[]\\';
6
+ const escaped = escapeRegex(metacharacters);
7
+ expect(escaped).toBe('\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\\\');
8
+ // The escaped string should be safe to use in a RegExp
9
+ expect(() => new RegExp(escaped)).not.toThrow();
10
+ });
11
+ it('leaves alphanumeric strings unchanged', () => {
12
+ expect(escapeRegex('hello123')).toBe('hello123');
13
+ expect(escapeRegex('FooBar')).toBe('FooBar');
14
+ });
15
+ it('handles empty string', () => {
16
+ expect(escapeRegex('')).toBe('');
17
+ });
18
+ });
19
+ describe('codeToPattern', () => {
20
+ it('escapes special characters and normalizes whitespace', () => {
21
+ const code = ' function test(a, b) {';
22
+ const pattern = codeToPattern(code);
23
+ // Parens and braces should be escaped
24
+ expect(pattern).toContain('\\(');
25
+ expect(pattern).toContain('\\)');
26
+ expect(pattern).toContain('\\{');
27
+ // Whitespace runs should become \s+
28
+ expect(pattern).toContain('\\s+');
29
+ // Should not contain literal spaces
30
+ expect(pattern).not.toContain(' ');
31
+ });
32
+ it('handles multi-line code', () => {
33
+ const code = 'if (x) {\n return y;\n}';
34
+ const pattern = codeToPattern(code);
35
+ // Should produce a valid regex
36
+ expect(() => new RegExp(pattern)).not.toThrow();
37
+ // Newlines and surrounding spaces should become \s+
38
+ expect(pattern).toContain('\\s+');
39
+ });
40
+ it('trims blank leading and trailing lines', () => {
41
+ const code = '\n\n const x = 1;\n\n';
42
+ const pattern = codeToPattern(code);
43
+ // Should not start or end with \s+
44
+ expect(pattern).not.toMatch(/^\\s\+/);
45
+ expect(pattern).not.toMatch(/\\s\+$/);
46
+ // Should contain the core content
47
+ expect(pattern).toContain('const');
48
+ });
49
+ it('returns empty string for whitespace-only input', () => {
50
+ expect(codeToPattern('')).toBe('');
51
+ expect(codeToPattern(' ')).toBe('');
52
+ expect(codeToPattern('\n\n\n')).toBe('');
53
+ expect(codeToPattern(' \t \n ')).toBe('');
54
+ });
55
+ it('generated pattern actually matches the original code', () => {
56
+ const code = 'const result = foo(bar, baz);';
57
+ const pattern = codeToPattern(code);
58
+ const re = new RegExp(pattern);
59
+ expect(re.test(code)).toBe(true);
60
+ });
61
+ it('generated pattern matches reformatted version', () => {
62
+ const code = 'const x = 1';
63
+ const pattern = codeToPattern(code);
64
+ const re = new RegExp(pattern);
65
+ // Original (double space)
66
+ expect(re.test('const x = 1')).toBe(true);
67
+ // Reformatted (single space)
68
+ expect(re.test('const x = 1')).toBe(true);
69
+ // Extra spaces
70
+ expect(re.test('const x = 1')).toBe(true);
71
+ });
72
+ });
73
+ //# sourceMappingURL=regex-utils.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regex-utils.test.js","sourceRoot":"","sources":["../src/regex-utils.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Semgrep YAML rule importer (Pipeline 4).
3
+ * Parses Semgrep rule files and produces CompiledRule objects.
4
+ */
5
+ import type { CompiledRule } from './compiler-schema.js';
6
+ export interface SemgrepImportResult {
7
+ rules: CompiledRule[];
8
+ skipped: {
9
+ id: string;
10
+ reason: string;
11
+ }[];
12
+ }
13
+ /** Parse Semgrep YAML rules into CompiledRule objects. */
14
+ export declare function parseSemgrepRules(yamlContent: string): SemgrepImportResult;
15
+ //# sourceMappingURL=semgrep-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semgrep-adapter.d.ts","sourceRoot":"","sources":["../src/semgrep-adapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AA8BzD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAID,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,CAqH1E"}