@runhalo/engine 0.4.0 → 0.6.0

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 (64) hide show
  1. package/dist/ast-engine.d.ts +60 -0
  2. package/dist/ast-engine.js +653 -0
  3. package/dist/ast-engine.js.map +1 -0
  4. package/dist/context-analyzer.d.ts +209 -0
  5. package/dist/context-analyzer.js +408 -0
  6. package/dist/context-analyzer.js.map +1 -0
  7. package/dist/data-flow-tracer.d.ts +106 -0
  8. package/dist/data-flow-tracer.js +506 -0
  9. package/dist/data-flow-tracer.js.map +1 -0
  10. package/dist/fp-patterns.d.ts +36 -0
  11. package/dist/fp-patterns.js +426 -0
  12. package/dist/fp-patterns.js.map +1 -0
  13. package/dist/frameworks/angular.d.ts +11 -0
  14. package/dist/frameworks/angular.js +41 -0
  15. package/dist/frameworks/angular.js.map +1 -0
  16. package/dist/frameworks/django.d.ts +11 -0
  17. package/dist/frameworks/django.js +57 -0
  18. package/dist/frameworks/django.js.map +1 -0
  19. package/dist/frameworks/index.d.ts +59 -0
  20. package/dist/frameworks/index.js +99 -0
  21. package/dist/frameworks/index.js.map +1 -0
  22. package/dist/frameworks/nextjs.d.ts +11 -0
  23. package/dist/frameworks/nextjs.js +59 -0
  24. package/dist/frameworks/nextjs.js.map +1 -0
  25. package/dist/frameworks/rails.d.ts +11 -0
  26. package/dist/frameworks/rails.js +58 -0
  27. package/dist/frameworks/rails.js.map +1 -0
  28. package/dist/frameworks/react.d.ts +13 -0
  29. package/dist/frameworks/react.js +36 -0
  30. package/dist/frameworks/react.js.map +1 -0
  31. package/dist/frameworks/types.d.ts +29 -0
  32. package/dist/frameworks/types.js +11 -0
  33. package/dist/frameworks/types.js.map +1 -0
  34. package/dist/frameworks/vue.d.ts +9 -0
  35. package/dist/frameworks/vue.js +39 -0
  36. package/dist/frameworks/vue.js.map +1 -0
  37. package/dist/graduation/fp-verdict-logger.d.ts +81 -0
  38. package/dist/graduation/fp-verdict-logger.js +130 -0
  39. package/dist/graduation/fp-verdict-logger.js.map +1 -0
  40. package/dist/graduation/graduation-codifier.d.ts +37 -0
  41. package/dist/graduation/graduation-codifier.js +205 -0
  42. package/dist/graduation/graduation-codifier.js.map +1 -0
  43. package/dist/graduation/graduation-validator.d.ts +73 -0
  44. package/dist/graduation/graduation-validator.js +204 -0
  45. package/dist/graduation/graduation-validator.js.map +1 -0
  46. package/dist/graduation/index.d.ts +71 -0
  47. package/dist/graduation/index.js +105 -0
  48. package/dist/graduation/index.js.map +1 -0
  49. package/dist/graduation/pattern-aggregator.d.ts +77 -0
  50. package/dist/graduation/pattern-aggregator.js +154 -0
  51. package/dist/graduation/pattern-aggregator.js.map +1 -0
  52. package/dist/index.d.ts +99 -0
  53. package/dist/index.js +718 -61
  54. package/dist/index.js.map +1 -1
  55. package/dist/review-board/two-agent-review.d.ts +152 -0
  56. package/dist/review-board/two-agent-review.js +463 -0
  57. package/dist/review-board/two-agent-review.js.map +1 -0
  58. package/dist/scope-analyzer.d.ts +91 -0
  59. package/dist/scope-analyzer.js +300 -0
  60. package/dist/scope-analyzer.js.map +1 -0
  61. package/package.json +9 -2
  62. package/rules/coppa-tier-1.yaml +17 -10
  63. package/rules/rules.json +2094 -99
  64. package/rules/validation-report.json +58 -0
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ /**
3
+ * Halo Scope Analyzer
4
+ * Determines the context of code being scanned to reduce false positives.
5
+ *
6
+ * Analyzes WHERE code lives (test file? admin route? type definition?)
7
+ * so that rule violations can be weighted or suppressed based on context.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ScopeAnalyzer = void 0;
11
+ // ---------------------------------------------------------------------------
12
+ // File path patterns
13
+ // ---------------------------------------------------------------------------
14
+ const TEST_FILE_PATTERNS = [
15
+ /_test\.go$/i,
16
+ /\.spec\.[tj]sx?$/i,
17
+ /\.test\.[tj]sx?$/i,
18
+ /(^|[/\\])__tests__[/\\]/i,
19
+ /(^|[/\\])test[/\\]/i,
20
+ /(^|[/\\])tests[/\\]/i,
21
+ /conftest\.py$/i,
22
+ /(^|[/\\])Test[A-Z][^/\\]*\.java$/,
23
+ ];
24
+ const ADMIN_PATH_PATTERNS = [
25
+ /[/\\]admin[/\\]/i,
26
+ /[/\\]admin\.[a-z]+$/i,
27
+ /[/\\]dashboard[/\\]/i,
28
+ ];
29
+ const ADMIN_CONTENT_PATTERNS = [
30
+ /\bAdminPanel\b/,
31
+ /\bAdminDashboard\b/,
32
+ /\bAdminLayout\b/,
33
+ /\bAdminRoute\b/,
34
+ /\bAdminPage\b/,
35
+ ];
36
+ const USER_FACING_PATTERNS = [
37
+ /[/\\]pages[/\\]/i,
38
+ /[/\\]components[/\\]/i,
39
+ /[/\\]views[/\\]/i,
40
+ /[/\\]screens[/\\]/i,
41
+ /[/\\]app[/\\]/i,
42
+ /[/\\]src[/\\]components[/\\]/i,
43
+ ];
44
+ const TYPE_DEF_PATH_PATTERNS = [
45
+ /\.d\.tsx?$/i,
46
+ ];
47
+ const CONFIG_FILE_PATTERNS = [
48
+ /\.config\.[tj]sx?$/i,
49
+ /\.config\.[cm]?js$/i,
50
+ /(^|[/\\])config[/\\]/i,
51
+ /(^|[/\\])settings\.py$/i,
52
+ /(^|[/\\])\.env(\.|$)/i,
53
+ /\.config\./i,
54
+ ];
55
+ // ---------------------------------------------------------------------------
56
+ // AST node types used for line context analysis
57
+ // ---------------------------------------------------------------------------
58
+ const INTERFACE_NODE_TYPES = new Set([
59
+ 'interface_declaration',
60
+ ]);
61
+ const FUNCTION_NODE_TYPES = new Set([
62
+ 'function_declaration',
63
+ 'arrow_function',
64
+ 'function',
65
+ 'method_definition',
66
+ 'function_expression',
67
+ ]);
68
+ const CLASS_NODE_TYPES = new Set([
69
+ 'class_declaration',
70
+ 'class',
71
+ ]);
72
+ const METHOD_NODE_TYPES = new Set([
73
+ 'method_definition',
74
+ ]);
75
+ const JSX_NODE_TYPES = new Set([
76
+ 'jsx_element',
77
+ 'jsx_self_closing_element',
78
+ ]);
79
+ // ---------------------------------------------------------------------------
80
+ // ScopeAnalyzer
81
+ // ---------------------------------------------------------------------------
82
+ /**
83
+ * Analyzes the scope/context of source files and individual lines
84
+ * to help the engine make smarter decisions about rule applicability.
85
+ */
86
+ class ScopeAnalyzer {
87
+ /**
88
+ * Analyze file-level scope from path heuristics and optionally content/AST.
89
+ *
90
+ * @param filePath - Relative or absolute path to the source file
91
+ * @param content - Full text content of the file
92
+ * @param tree - Optional tree-sitter AST (used for deeper analysis)
93
+ * @returns ScopeContext describing the file's role
94
+ */
95
+ analyzeFile(filePath, content, tree) {
96
+ const normalized = filePath.replace(/\\/g, '/');
97
+ return {
98
+ isTestFile: this.detectTestFile(normalized),
99
+ isAdminRoute: this.detectAdminRoute(normalized, content),
100
+ isUserFacing: this.detectUserFacing(normalized),
101
+ isTypeDefinition: this.detectTypeDefinition(normalized, content, tree),
102
+ isConfigFile: this.detectConfigFile(normalized),
103
+ };
104
+ }
105
+ /**
106
+ * Analyze what AST construct encloses a given line.
107
+ *
108
+ * Walks the tree to find the deepest node that contains the target line,
109
+ * then walks upward through ancestors to determine enclosing constructs.
110
+ *
111
+ * @param line - 1-based line number
112
+ * @param tree - tree-sitter AST
113
+ * @returns LineContext describing the enclosing constructs
114
+ */
115
+ analyzeLineContext(line, tree) {
116
+ const context = {
117
+ inInterfaceDecl: false,
118
+ inFunctionBody: false,
119
+ inClassMethod: false,
120
+ inJSXElement: false,
121
+ };
122
+ // tree-sitter uses 0-based row indices
123
+ const targetRow = line - 1;
124
+ const node = this.findDeepestNodeAtLine(tree.rootNode, targetRow);
125
+ if (!node) {
126
+ return context;
127
+ }
128
+ // Walk ancestors from the deepest node upward
129
+ let current = node;
130
+ let insideClassBody = false;
131
+ while (current) {
132
+ const type = current.type;
133
+ if (INTERFACE_NODE_TYPES.has(type)) {
134
+ context.inInterfaceDecl = true;
135
+ }
136
+ if (FUNCTION_NODE_TYPES.has(type)) {
137
+ context.inFunctionBody = true;
138
+ // If we already saw a class ancestor, this function is a class method
139
+ if (insideClassBody || METHOD_NODE_TYPES.has(type)) {
140
+ context.inClassMethod = true;
141
+ }
142
+ }
143
+ if (CLASS_NODE_TYPES.has(type)) {
144
+ insideClassBody = true;
145
+ }
146
+ if (JSX_NODE_TYPES.has(type)) {
147
+ context.inJSXElement = true;
148
+ }
149
+ current = current.parent;
150
+ }
151
+ return context;
152
+ }
153
+ // -----------------------------------------------------------------------
154
+ // Private helpers — file scope detection
155
+ // -----------------------------------------------------------------------
156
+ detectTestFile(filePath) {
157
+ return TEST_FILE_PATTERNS.some(p => p.test(filePath));
158
+ }
159
+ detectAdminRoute(filePath, content) {
160
+ // Path-based detection
161
+ if (ADMIN_PATH_PATTERNS.some(p => p.test(filePath))) {
162
+ return true;
163
+ }
164
+ // Content-based detection (e.g., AdminPanel component)
165
+ if (ADMIN_CONTENT_PATTERNS.some(p => p.test(content))) {
166
+ // Only count if the path also suggests dashboard/admin context
167
+ // OR the component name is strongly admin-specific
168
+ return true;
169
+ }
170
+ return false;
171
+ }
172
+ detectUserFacing(filePath) {
173
+ return USER_FACING_PATTERNS.some(p => p.test(filePath));
174
+ }
175
+ detectTypeDefinition(filePath, content, tree) {
176
+ // .d.ts files are always type definitions
177
+ if (TYPE_DEF_PATH_PATTERNS.some(p => p.test(filePath))) {
178
+ return true;
179
+ }
180
+ // Heuristic: if the file content is predominantly interfaces/types
181
+ if (tree) {
182
+ return this.isAstPrimarilyTypes(tree);
183
+ }
184
+ // Fallback: content-based heuristic for non-AST path
185
+ return this.isContentPrimarilyTypes(content);
186
+ }
187
+ detectConfigFile(filePath) {
188
+ return CONFIG_FILE_PATTERNS.some(p => p.test(filePath));
189
+ }
190
+ // -----------------------------------------------------------------------
191
+ // Private helpers — type definition heuristics
192
+ // -----------------------------------------------------------------------
193
+ /**
194
+ * Check via AST whether the file consists primarily of type declarations.
195
+ * A file is considered primarily types if >= 80% of its top-level
196
+ * statements are interface/type declarations.
197
+ */
198
+ isAstPrimarilyTypes(tree) {
199
+ const root = tree.rootNode;
200
+ let totalStatements = 0;
201
+ let typeStatements = 0;
202
+ for (let i = 0; i < root.childCount; i++) {
203
+ const child = root.child(i);
204
+ if (!child)
205
+ continue;
206
+ const type = child.type;
207
+ // Skip import/export wrapper nodes — look inside them
208
+ if (type === 'export_statement') {
209
+ const inner = child.namedChildren[0];
210
+ if (inner) {
211
+ totalStatements++;
212
+ if (this.isTypeNode(inner.type)) {
213
+ typeStatements++;
214
+ }
215
+ }
216
+ continue;
217
+ }
218
+ // Skip import statements — they don't count
219
+ if (type === 'import_statement')
220
+ continue;
221
+ totalStatements++;
222
+ if (this.isTypeNode(type)) {
223
+ typeStatements++;
224
+ }
225
+ }
226
+ if (totalStatements === 0)
227
+ return false;
228
+ return typeStatements / totalStatements >= 0.8;
229
+ }
230
+ isTypeNode(nodeType) {
231
+ return (nodeType === 'interface_declaration' ||
232
+ nodeType === 'type_alias_declaration');
233
+ }
234
+ /**
235
+ * Content-based fallback: count top-level interface/type declaration blocks
236
+ * vs total top-level statements (excluding imports and blanks).
237
+ *
238
+ * Uses a simple state machine: lines starting with `interface` or `type`
239
+ * keywords begin a type block. Lines that are clearly function/class/const
240
+ * declarations begin a non-type block. Lines inside a block (e.g., interface
241
+ * members) are attributed to whatever block they belong to.
242
+ */
243
+ isContentPrimarilyTypes(content) {
244
+ const lines = content.split('\n');
245
+ let typeBlocks = 0;
246
+ let nonTypeBlocks = 0;
247
+ const typeStart = /^\s*(export\s+)?(interface|type)\s+\w+/;
248
+ const nonTypeStart = /^\s*(export\s+)?(function|class|const|let|var|enum|async\s+function)\s+\w+/;
249
+ const importPattern = /^\s*import\s/;
250
+ const blankOrComment = /^\s*(\/\/|\/\*|\*|$)/;
251
+ const closingBrace = /^\s*\};\s*$/;
252
+ for (const line of lines) {
253
+ if (blankOrComment.test(line))
254
+ continue;
255
+ if (importPattern.test(line))
256
+ continue;
257
+ if (closingBrace.test(line))
258
+ continue;
259
+ if (typeStart.test(line)) {
260
+ typeBlocks++;
261
+ }
262
+ else if (nonTypeStart.test(line)) {
263
+ nonTypeBlocks++;
264
+ }
265
+ // Lines inside a block (members, etc.) are ignored for counting
266
+ }
267
+ const totalBlocks = typeBlocks + nonTypeBlocks;
268
+ if (totalBlocks === 0)
269
+ return false;
270
+ return typeBlocks / totalBlocks >= 0.8;
271
+ }
272
+ // -----------------------------------------------------------------------
273
+ // Private helpers — AST traversal
274
+ // -----------------------------------------------------------------------
275
+ /**
276
+ * Find the deepest (most specific) AST node whose range contains the
277
+ * target row. This gives us a starting point for ancestor walking.
278
+ */
279
+ findDeepestNodeAtLine(node, targetRow) {
280
+ // If this node doesn't span the target row, skip it
281
+ if (node.startPosition.row > targetRow || node.endPosition.row < targetRow) {
282
+ return null;
283
+ }
284
+ // Try to find a more specific child
285
+ for (let i = 0; i < node.childCount; i++) {
286
+ const child = node.child(i);
287
+ if (!child)
288
+ continue;
289
+ const deeper = this.findDeepestNodeAtLine(child, targetRow);
290
+ if (deeper) {
291
+ return deeper;
292
+ }
293
+ }
294
+ // No child matched — this node is the deepest
295
+ return node;
296
+ }
297
+ }
298
+ exports.ScopeAnalyzer = ScopeAnalyzer;
299
+ exports.default = ScopeAnalyzer;
300
+ //# sourceMappingURL=scope-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-analyzer.js","sourceRoot":"","sources":["../src/scope-analyzer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@runhalo/engine",
3
- "version": "0.4.0",
4
- "description": "Halo rule engine — COPPA 2.0 risk detection via tree-sitter AST analysis",
3
+ "version": "0.6.0",
4
+ "description": "Halo rule engine — child online safety compliance detection. 130 rules across 10 packs covering COPPA, UK AADC, EU DSA, EU AI Act, and more.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
@@ -17,6 +17,10 @@
17
17
  "coppa",
18
18
  "privacy",
19
19
  "child-safety",
20
+ "compliance",
21
+ "aadc",
22
+ "dsa",
23
+ "online-safety",
20
24
  "static-analysis",
21
25
  "tree-sitter",
22
26
  "ast",
@@ -33,6 +37,9 @@
33
37
  },
34
38
  "author": "Mindful Media <hello@mindfulmedia.org> (https://mindfulmedia.org)",
35
39
  "license": "Apache-2.0",
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
36
43
  "dependencies": {
37
44
  "glob": "^10.3.10",
38
45
  "js-yaml": "^3.14.2",
@@ -13,7 +13,7 @@ rules:
13
13
  name: Unverified Social Login Providers
14
14
  coppaSection: "§ 312.5 (Parental Consent) & COPPA 2.0 § 312.11 (Mixed Audience)"
15
15
  severity: critical
16
- penaltyRange: "$51,744 per violation"
16
+ penaltyRange: "$53,088 per violation"
17
17
  languages:
18
18
  - typescript
19
19
  - javascript
@@ -46,7 +46,7 @@ rules:
46
46
  name: Sensitive Data in GET Requests
47
47
  coppaSection: "§ 312.2 (Definitions - Personal Information)"
48
48
  severity: high
49
- penaltyRange: "$51,744 per violation"
49
+ penaltyRange: "$53,088 per violation"
50
50
  languages:
51
51
  - typescript
52
52
  - javascript
@@ -80,7 +80,7 @@ rules:
80
80
  name: Restricted Third-Party Ad Trackers
81
81
  coppaSection: "§ 312.2 (Definitions - Support for Internal Operations) - COPPA 2.0 Focus"
82
82
  severity: critical
83
- penaltyRange: "$51,744 per violation"
83
+ penaltyRange: "$53,088 per violation"
84
84
  languages:
85
85
  - typescript
86
86
  - javascript
@@ -123,7 +123,7 @@ rules:
123
123
  name: Precise Geolocation Without Parental Consent
124
124
  coppaSection: "§ 312.2 (Personal Information)"
125
125
  severity: high
126
- penaltyRange: "$51,744 per violation"
126
+ penaltyRange: "$53,088 per violation"
127
127
  languages:
128
128
  - typescript
129
129
  - javascript
@@ -159,18 +159,23 @@ rules:
159
159
 
160
160
  # ============================================
161
161
  # 5. coppa-retention-005: Missing Data Retention Policy
162
+ # Updated: 2026-03-12 — COPPA 2025 final rule explicitly
163
+ # prohibits indefinite retention (§ 312.10)
162
164
  # ============================================
163
165
  - metadata:
164
166
  id: coppa-retention-005
165
167
  name: Missing Data Retention Policy in Database Schemas
166
- coppaSection: "§ 312.10 (Data Retention and Deletion) - COPPA 2.0 Focus"
168
+ coppaSection: "§ 312.10 (Data Retention and Deletion) - COPPA 2025 Final Rule"
167
169
  severity: medium
168
- penaltyRange: "Regulatory scrutiny / Audit failure"
170
+ penaltyRange: "$53,088 per violation (indefinite retention prohibition)"
169
171
  languages:
170
172
  - typescript
171
173
  - javascript
172
174
  - python
173
175
  - sql
176
+ - go
177
+ - java
178
+ - kotlin
174
179
  falsePositiveRisk: high
175
180
  patterns:
176
181
  - regex:
@@ -186,17 +191,19 @@ rules:
186
191
  pattern: "class\\s+\\w+.*extends\\s+Model(?!.*deletedAt)"
187
192
  flags: "g"
188
193
  autoFix:
189
- description: "Add TTL index or expiration field to schema"
194
+ description: "Add explicit retention period, TTL index, and deletion mechanism per COPPA 2025 § 312.10"
190
195
  replacement: |
191
- // Mongoose - Add TTL index
196
+ // Mongoose - Add TTL index with explicit retention period
192
197
  new Schema({
193
198
  createdAt: { type: Date, expires: '365d' },
199
+ retentionDays: { type: Number, default: 365 },
194
200
  email: String
195
201
  });
196
-
197
- // Sequelize - Add deletedAt
202
+
203
+ // Sequelize - Add deletedAt with retention config
198
204
  sequelize.define('User', {
199
205
  deletedAt: { type: DataTypes.DATE },
206
+ retentionDays: { type: DataTypes.INTEGER, defaultValue: 365 },
200
207
  // ... other fields
201
208
  }, {
202
209
  paranoid: true