@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
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=semgrep-adapter.test.d.ts.map
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmnto/totem",
3
- "version": "1.8.3",
3
+ "version": "1.8.5",
4
4
  "description": "Persistent memory and context layer for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",