@mmnto/totem 1.8.2 → 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.
- package/dist/compile-lesson.d.ts +2 -0
- package/dist/compile-lesson.d.ts.map +1 -1
- package/dist/compile-lesson.js +58 -2
- package/dist/compile-lesson.js.map +1 -1
- package/dist/compile-lesson.test.js +143 -0
- package/dist/compile-lesson.test.js.map +1 -1
- package/dist/eslint-adapter.d.ts +18 -0
- package/dist/eslint-adapter.d.ts.map +1 -0
- package/dist/eslint-adapter.js +173 -0
- package/dist/eslint-adapter.js.map +1 -0
- package/dist/eslint-adapter.test.d.ts +2 -0
- package/dist/eslint-adapter.test.d.ts.map +1 -0
- package/dist/eslint-adapter.test.js +110 -0
- package/dist/eslint-adapter.test.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/lesson-linter.d.ts.map +1 -1
- package/dist/lesson-linter.js +50 -1
- package/dist/lesson-linter.js.map +1 -1
- package/dist/lesson-linter.test.js +63 -0
- package/dist/lesson-linter.test.js.map +1 -1
- package/dist/lesson-pattern.d.ts +9 -0
- package/dist/lesson-pattern.d.ts.map +1 -1
- package/dist/lesson-pattern.js +30 -0
- package/dist/lesson-pattern.js.map +1 -1
- package/dist/lesson-pattern.test.js +91 -1
- package/dist/lesson-pattern.test.js.map +1 -1
- package/dist/pipeline-observation.d.ts +36 -0
- package/dist/pipeline-observation.d.ts.map +1 -0
- package/dist/pipeline-observation.js +100 -0
- package/dist/pipeline-observation.js.map +1 -0
- package/dist/pipeline-observation.test.d.ts +2 -0
- package/dist/pipeline-observation.test.d.ts.map +1 -0
- package/dist/pipeline-observation.test.js +172 -0
- package/dist/pipeline-observation.test.js.map +1 -0
- package/dist/regex-utils.d.ts +24 -0
- package/dist/regex-utils.d.ts.map +1 -0
- package/dist/regex-utils.js +39 -0
- package/dist/regex-utils.js.map +1 -0
- package/dist/regex-utils.test.d.ts +2 -0
- package/dist/regex-utils.test.d.ts.map +1 -0
- package/dist/regex-utils.test.js +73 -0
- package/dist/regex-utils.test.js.map +1 -0
- package/dist/semgrep-adapter.d.ts +15 -0
- package/dist/semgrep-adapter.d.ts.map +1 -0
- package/dist/semgrep-adapter.js +133 -0
- package/dist/semgrep-adapter.js.map +1 -0
- package/dist/semgrep-adapter.test.d.ts +2 -0
- package/dist/semgrep-adapter.test.d.ts.map +1 -0
- package/dist/semgrep-adapter.test.js +136 -0
- package/dist/semgrep-adapter.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semgrep YAML rule importer (Pipeline 4).
|
|
3
|
+
* Parses Semgrep rule files and produces CompiledRule objects.
|
|
4
|
+
*/
|
|
5
|
+
import { parse as parseYaml } from 'yaml';
|
|
6
|
+
import { hashLesson, validateRegex } from './compiler.js';
|
|
7
|
+
// ─── Language-to-glob mapping ───────────────────────
|
|
8
|
+
const LANGUAGE_GLOBS = {
|
|
9
|
+
javascript: ['**/*.js', '**/*.jsx'],
|
|
10
|
+
typescript: ['**/*.ts', '**/*.tsx'],
|
|
11
|
+
js: ['**/*.js', '**/*.jsx'],
|
|
12
|
+
ts: ['**/*.ts', '**/*.tsx'],
|
|
13
|
+
python: ['**/*.py'],
|
|
14
|
+
rust: ['**/*.rs'],
|
|
15
|
+
go: ['**/*.go'],
|
|
16
|
+
java: ['**/*.java'],
|
|
17
|
+
ruby: ['**/*.rb'],
|
|
18
|
+
c: ['**/*.c', '**/*.h'],
|
|
19
|
+
cpp: ['**/*.cpp', '**/*.hpp', '**/*.cc'],
|
|
20
|
+
};
|
|
21
|
+
function languagesToGlobs(languages) {
|
|
22
|
+
if (!languages || languages.length === 0)
|
|
23
|
+
return undefined;
|
|
24
|
+
const globs = [];
|
|
25
|
+
for (const lang of languages) {
|
|
26
|
+
const mapped = LANGUAGE_GLOBS[lang.toLowerCase()];
|
|
27
|
+
if (mapped)
|
|
28
|
+
globs.push(...mapped);
|
|
29
|
+
}
|
|
30
|
+
return globs.length > 0 ? globs : undefined;
|
|
31
|
+
}
|
|
32
|
+
// ─── Parser ─────────────────────────────────────────
|
|
33
|
+
/** Parse Semgrep YAML rules into CompiledRule objects. */
|
|
34
|
+
export function parseSemgrepRules(yamlContent) {
|
|
35
|
+
const rules = [];
|
|
36
|
+
const skipped = [];
|
|
37
|
+
let doc;
|
|
38
|
+
try {
|
|
39
|
+
doc = parseYaml(yamlContent);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return { rules, skipped: [{ id: '(root)', reason: 'Invalid YAML' }] };
|
|
43
|
+
}
|
|
44
|
+
if (!doc ||
|
|
45
|
+
typeof doc !== 'object' ||
|
|
46
|
+
!('rules' in doc) ||
|
|
47
|
+
!Array.isArray(doc.rules)) {
|
|
48
|
+
return { rules, skipped: [{ id: '(root)', reason: 'No "rules" array found in YAML' }] };
|
|
49
|
+
}
|
|
50
|
+
const now = new Date().toISOString();
|
|
51
|
+
for (const entry of doc.rules) {
|
|
52
|
+
if (!entry || typeof entry !== 'object')
|
|
53
|
+
continue;
|
|
54
|
+
const rule = entry;
|
|
55
|
+
const id = typeof rule.id === 'string' ? rule.id : undefined;
|
|
56
|
+
if (!id) {
|
|
57
|
+
skipped.push({ id: '(unknown)', reason: 'Missing rule id' });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Extract pattern — prefer pattern-regex, fall back to simple string pattern
|
|
61
|
+
let pattern;
|
|
62
|
+
if (typeof rule['pattern-regex'] === 'string') {
|
|
63
|
+
pattern = rule['pattern-regex'];
|
|
64
|
+
}
|
|
65
|
+
else if (typeof rule.pattern === 'string' && !rule.patterns && !rule['pattern-either']) {
|
|
66
|
+
// Simple string patterns like "eval(...)" — convert to regex
|
|
67
|
+
// 1. Replace metavariables with placeholder before escaping
|
|
68
|
+
// 2. Escape regex special chars
|
|
69
|
+
// 3. Restore metavariable placeholders and `...` wildcards
|
|
70
|
+
const METAVAR_PLACEHOLDER = '\x00METAVAR\x00';
|
|
71
|
+
const withPlaceholders = rule.pattern.replace(/\$\w+/g, METAVAR_PLACEHOLDER);
|
|
72
|
+
const escaped = withPlaceholders.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
73
|
+
pattern = escaped
|
|
74
|
+
.replace(/\\\.\\\.\\\./g, '.*')
|
|
75
|
+
.replace(new RegExp(METAVAR_PLACEHOLDER.replace(/\x00/g, '\\x00'), 'g'), '\\w+');
|
|
76
|
+
}
|
|
77
|
+
if (!pattern) {
|
|
78
|
+
const reason = rule.patterns
|
|
79
|
+
? 'Compound pattern (patterns/pattern-either)'
|
|
80
|
+
: 'No pattern or pattern-regex field';
|
|
81
|
+
skipped.push({ id, reason });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// Validate regex for syntax and ReDoS safety
|
|
85
|
+
const validation = validateRegex(pattern);
|
|
86
|
+
if (!validation.valid) {
|
|
87
|
+
skipped.push({ id, reason: `Invalid regex: ${validation.reason}` });
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const message = typeof rule.message === 'string' ? rule.message : `[semgrep] ${id}`;
|
|
91
|
+
// Severity
|
|
92
|
+
const sevRaw = typeof rule.severity === 'string' ? rule.severity.toUpperCase() : 'WARNING';
|
|
93
|
+
const severity = sevRaw === 'ERROR' ? 'error' : 'warning';
|
|
94
|
+
// File globs from languages + paths
|
|
95
|
+
const languages = Array.isArray(rule.languages)
|
|
96
|
+
? rule.languages.filter((l) => typeof l === 'string')
|
|
97
|
+
: undefined;
|
|
98
|
+
const langGlobs = languagesToGlobs(languages);
|
|
99
|
+
const paths = rule.paths;
|
|
100
|
+
const includeGlobs = paths && Array.isArray(paths.include)
|
|
101
|
+
? paths.include.filter((p) => typeof p === 'string')
|
|
102
|
+
: [];
|
|
103
|
+
const excludeGlobs = paths && Array.isArray(paths.exclude)
|
|
104
|
+
? paths.exclude.filter((p) => typeof p === 'string').map((p) => `!${p}`)
|
|
105
|
+
: [];
|
|
106
|
+
const fileGlobs = [
|
|
107
|
+
...(includeGlobs.length > 0 ? includeGlobs : (langGlobs ?? [])),
|
|
108
|
+
...excludeGlobs,
|
|
109
|
+
];
|
|
110
|
+
// Category from metadata (validate against allowed values)
|
|
111
|
+
const VALID_CATEGORIES = new Set(['security', 'architecture', 'style', 'performance']);
|
|
112
|
+
const metadata = rule.metadata;
|
|
113
|
+
const rawCategory = metadata && typeof metadata.category === 'string' ? metadata.category : undefined;
|
|
114
|
+
const category = rawCategory && VALID_CATEGORIES.has(rawCategory)
|
|
115
|
+
? rawCategory
|
|
116
|
+
: undefined;
|
|
117
|
+
const heading = `[semgrep] ${id}`;
|
|
118
|
+
rules.push({
|
|
119
|
+
lessonHash: hashLesson(heading, message),
|
|
120
|
+
lessonHeading: heading,
|
|
121
|
+
pattern,
|
|
122
|
+
message,
|
|
123
|
+
engine: 'regex',
|
|
124
|
+
severity,
|
|
125
|
+
compiledAt: now,
|
|
126
|
+
createdAt: now,
|
|
127
|
+
...(fileGlobs.length > 0 ? { fileGlobs } : {}),
|
|
128
|
+
...(category ? { category } : {}),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return { rules, skipped };
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=semgrep-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semgrep-adapter.js","sourceRoot":"","sources":["../src/semgrep-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semgrep-adapter.test.d.ts","sourceRoot":"","sources":["../src/semgrep-adapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseSemgrepRules } from './semgrep-adapter.js';
|
|
3
|
+
describe('parseSemgrepRules', () => {
|
|
4
|
+
it('parses rule with pattern-regex', () => {
|
|
5
|
+
const yaml = `
|
|
6
|
+
rules:
|
|
7
|
+
- id: no-eval
|
|
8
|
+
pattern-regex: "\\\\beval\\\\s*\\\\("
|
|
9
|
+
message: Do not use eval()
|
|
10
|
+
severity: ERROR
|
|
11
|
+
languages: [javascript, typescript]
|
|
12
|
+
`;
|
|
13
|
+
const result = parseSemgrepRules(yaml);
|
|
14
|
+
expect(result.rules).toHaveLength(1);
|
|
15
|
+
expect(result.rules[0].lessonHeading).toBe('[semgrep] no-eval');
|
|
16
|
+
expect(result.rules[0].pattern).toBe('\\beval\\s*\\(');
|
|
17
|
+
expect(result.rules[0].severity).toBe('error');
|
|
18
|
+
expect(result.rules[0].engine).toBe('regex');
|
|
19
|
+
expect(result.rules[0].fileGlobs).toEqual(['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx']);
|
|
20
|
+
});
|
|
21
|
+
it('parses rule with simple string pattern', () => {
|
|
22
|
+
const yaml = `
|
|
23
|
+
rules:
|
|
24
|
+
- id: no-print
|
|
25
|
+
pattern: "print(...)"
|
|
26
|
+
message: Use logging module instead of print
|
|
27
|
+
severity: WARNING
|
|
28
|
+
languages: [python]
|
|
29
|
+
`;
|
|
30
|
+
const result = parseSemgrepRules(yaml);
|
|
31
|
+
expect(result.rules).toHaveLength(1);
|
|
32
|
+
expect(result.rules[0].pattern).toContain('print');
|
|
33
|
+
expect(result.rules[0].pattern).toContain('.*'); // ... converted to .*
|
|
34
|
+
expect(result.rules[0].severity).toBe('warning');
|
|
35
|
+
});
|
|
36
|
+
it('maps severity correctly', () => {
|
|
37
|
+
const yaml = `
|
|
38
|
+
rules:
|
|
39
|
+
- id: rule-error
|
|
40
|
+
pattern-regex: "err"
|
|
41
|
+
message: Error rule
|
|
42
|
+
severity: ERROR
|
|
43
|
+
- id: rule-warn
|
|
44
|
+
pattern-regex: "warn"
|
|
45
|
+
message: Warn rule
|
|
46
|
+
severity: WARNING
|
|
47
|
+
- id: rule-info
|
|
48
|
+
pattern-regex: "info"
|
|
49
|
+
message: Info rule
|
|
50
|
+
severity: INFO
|
|
51
|
+
`;
|
|
52
|
+
const result = parseSemgrepRules(yaml);
|
|
53
|
+
expect(result.rules[0].severity).toBe('error');
|
|
54
|
+
expect(result.rules[1].severity).toBe('warning');
|
|
55
|
+
expect(result.rules[2].severity).toBe('warning'); // INFO maps to warning
|
|
56
|
+
});
|
|
57
|
+
it('handles paths.include and paths.exclude', () => {
|
|
58
|
+
const yaml = `
|
|
59
|
+
rules:
|
|
60
|
+
- id: scoped-rule
|
|
61
|
+
pattern-regex: "TODO"
|
|
62
|
+
message: No TODOs
|
|
63
|
+
severity: WARNING
|
|
64
|
+
paths:
|
|
65
|
+
include:
|
|
66
|
+
- "src/**/*.ts"
|
|
67
|
+
exclude:
|
|
68
|
+
- "**/*.test.ts"
|
|
69
|
+
`;
|
|
70
|
+
const result = parseSemgrepRules(yaml);
|
|
71
|
+
expect(result.rules[0].fileGlobs).toEqual(['src/**/*.ts', '!**/*.test.ts']);
|
|
72
|
+
});
|
|
73
|
+
it('skips compound patterns and reports them', () => {
|
|
74
|
+
const yaml = `
|
|
75
|
+
rules:
|
|
76
|
+
- id: compound-rule
|
|
77
|
+
patterns:
|
|
78
|
+
- pattern: "eval(...)"
|
|
79
|
+
- pattern-not: "safe_eval(...)"
|
|
80
|
+
message: Compound rule
|
|
81
|
+
severity: ERROR
|
|
82
|
+
`;
|
|
83
|
+
const result = parseSemgrepRules(yaml);
|
|
84
|
+
expect(result.rules).toHaveLength(0);
|
|
85
|
+
expect(result.skipped).toHaveLength(1);
|
|
86
|
+
expect(result.skipped[0].id).toBe('compound-rule');
|
|
87
|
+
expect(result.skipped[0].reason).toContain('Compound');
|
|
88
|
+
});
|
|
89
|
+
it('skips rules without pattern fields', () => {
|
|
90
|
+
const yaml = `
|
|
91
|
+
rules:
|
|
92
|
+
- id: no-pattern
|
|
93
|
+
message: No pattern
|
|
94
|
+
severity: WARNING
|
|
95
|
+
`;
|
|
96
|
+
const result = parseSemgrepRules(yaml);
|
|
97
|
+
expect(result.rules).toHaveLength(0);
|
|
98
|
+
expect(result.skipped).toHaveLength(1);
|
|
99
|
+
});
|
|
100
|
+
it('handles invalid YAML gracefully', () => {
|
|
101
|
+
const result = parseSemgrepRules('{{{{not yaml');
|
|
102
|
+
expect(result.rules).toHaveLength(0);
|
|
103
|
+
expect(result.skipped[0].reason).toContain('Invalid YAML');
|
|
104
|
+
});
|
|
105
|
+
it('handles missing rules array', () => {
|
|
106
|
+
const result = parseSemgrepRules('other: value');
|
|
107
|
+
expect(result.rules).toHaveLength(0);
|
|
108
|
+
expect(result.skipped[0].reason).toContain('No "rules" array');
|
|
109
|
+
});
|
|
110
|
+
it('produces deterministic lessonHash', () => {
|
|
111
|
+
const yaml = `
|
|
112
|
+
rules:
|
|
113
|
+
- id: deterministic
|
|
114
|
+
pattern-regex: "test"
|
|
115
|
+
message: Test rule
|
|
116
|
+
severity: WARNING
|
|
117
|
+
`;
|
|
118
|
+
const r1 = parseSemgrepRules(yaml);
|
|
119
|
+
const r2 = parseSemgrepRules(yaml);
|
|
120
|
+
expect(r1.rules[0].lessonHash).toBe(r2.rules[0].lessonHash);
|
|
121
|
+
});
|
|
122
|
+
it('maps metadata.category', () => {
|
|
123
|
+
const yaml = `
|
|
124
|
+
rules:
|
|
125
|
+
- id: sec-rule
|
|
126
|
+
pattern-regex: "secret"
|
|
127
|
+
message: Secret detected
|
|
128
|
+
severity: ERROR
|
|
129
|
+
metadata:
|
|
130
|
+
category: security
|
|
131
|
+
`;
|
|
132
|
+
const result = parseSemgrepRules(yaml);
|
|
133
|
+
expect(result.rules[0].category).toBe('security');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
//# sourceMappingURL=semgrep-adapter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semgrep-adapter.test.js","sourceRoot":"","sources":["../src/semgrep-adapter.test.ts"],"names":[],"mappings":"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"}
|