@runhalo/engine 0.3.1 → 0.5.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.
- package/dist/ast-engine.d.ts +60 -0
- package/dist/ast-engine.js +653 -0
- package/dist/ast-engine.js.map +1 -0
- package/dist/context-analyzer.d.ts +209 -0
- package/dist/context-analyzer.js +401 -0
- package/dist/context-analyzer.js.map +1 -0
- package/dist/data-flow-tracer.d.ts +106 -0
- package/dist/data-flow-tracer.js +506 -0
- package/dist/data-flow-tracer.js.map +1 -0
- package/dist/frameworks/django.d.ts +11 -0
- package/dist/frameworks/django.js +57 -0
- package/dist/frameworks/django.js.map +1 -0
- package/dist/frameworks/index.d.ts +59 -0
- package/dist/frameworks/index.js +93 -0
- package/dist/frameworks/index.js.map +1 -0
- package/dist/frameworks/nextjs.d.ts +11 -0
- package/dist/frameworks/nextjs.js +59 -0
- package/dist/frameworks/nextjs.js.map +1 -0
- package/dist/frameworks/rails.d.ts +11 -0
- package/dist/frameworks/rails.js +58 -0
- package/dist/frameworks/rails.js.map +1 -0
- package/dist/frameworks/types.d.ts +29 -0
- package/dist/frameworks/types.js +11 -0
- package/dist/frameworks/types.js.map +1 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +235 -28
- package/dist/index.js.map +1 -1
- package/dist/scope-analyzer.d.ts +91 -0
- package/dist/scope-analyzer.js +300 -0
- package/dist/scope-analyzer.js.map +1 -0
- package/package.json +6 -2
- package/rules/rules.json +2632 -0
- package/rules/validation-report.json +58 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Django Framework Profile
|
|
4
|
+
*
|
|
5
|
+
* Django provides built-in protections that overlap with several COPPA rules:
|
|
6
|
+
* - Template engine auto-escapes all variables by default
|
|
7
|
+
* - SecurityMiddleware enforces HTTPS when configured
|
|
8
|
+
* - Built-in password validators enforce complexity requirements
|
|
9
|
+
* - django-lifecycle and django-reversion can handle data retention externally
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.djangoProfile = void 0;
|
|
13
|
+
exports.djangoProfile = {
|
|
14
|
+
id: 'django',
|
|
15
|
+
name: 'Django',
|
|
16
|
+
ecosystem: 'python',
|
|
17
|
+
handled_rules: [
|
|
18
|
+
{
|
|
19
|
+
rule_id: 'coppa-sec-015',
|
|
20
|
+
action: 'suppress',
|
|
21
|
+
reason: 'Django templates auto-escape all variables by default.',
|
|
22
|
+
documentation_url: 'https://docs.djangoproject.com/en/stable/ref/templates/language/#automatic-html-escaping',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
rule_id: 'coppa-retention-005',
|
|
26
|
+
action: 'downgrade',
|
|
27
|
+
downgrade_to: 'low',
|
|
28
|
+
reason: 'Django models with django-lifecycle or django-reversion may handle retention externally.',
|
|
29
|
+
documentation_url: 'https://docs.djangoproject.com/en/stable/topics/db/models/',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
rule_id: 'coppa-sec-006',
|
|
33
|
+
action: 'suppress',
|
|
34
|
+
reason: 'Django SecurityMiddleware enforces HTTPS when configured.',
|
|
35
|
+
documentation_url: 'https://docs.djangoproject.com/en/stable/ref/middleware/#module-django.middleware.security',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
rule_id: 'coppa-sec-010',
|
|
39
|
+
action: 'suppress',
|
|
40
|
+
reason: 'Django built-in password validators enforce complexity.',
|
|
41
|
+
documentation_url: 'https://docs.djangoproject.com/en/stable/topics/auth/passwords/#module-django.contrib.auth.password_validation',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
safe_patterns: [
|
|
45
|
+
{
|
|
46
|
+
description: 'Django CSRF middleware for cross-site request forgery protection',
|
|
47
|
+
patterns: [/CsrfViewMiddleware/, /csrf_token/],
|
|
48
|
+
applies_to_rules: ['coppa-sec-015'],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
description: 'Django admin interface with built-in auth and access controls',
|
|
52
|
+
patterns: [/admin\.site\.register/, /class.*Admin\(/, /admin\.py/],
|
|
53
|
+
applies_to_rules: ['coppa-auth-001', 'coppa-ui-008', 'coppa-default-020'],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=django.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"django.js","sourceRoot":"","sources":["../../src/frameworks/django.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAIU,QAAA,aAAa,GAAqB;IAC7C,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,QAAQ;IACd,SAAS,EAAE,QAAQ;IACnB,aAAa,EAAE;QACb;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,wDAAwD;YAChE,iBAAiB,EAAE,0FAA0F;SAC9G;QACD;YACE,OAAO,EAAE,qBAAqB;YAC9B,MAAM,EAAE,WAAW;YACnB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,0FAA0F;YAClG,iBAAiB,EAAE,4DAA4D;SAChF;QACD;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,2DAA2D;YACnE,iBAAiB,EAAE,4FAA4F;SAChH;QACD;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,yDAAyD;YACjE,iBAAiB,EAAE,gHAAgH;SACpI;KACF;IACD,aAAa,EAAE;QACb;YACE,WAAW,EAAE,kEAAkE;YAC/E,QAAQ,EAAE,CAAC,oBAAoB,EAAE,YAAY,CAAC;YAC9C,gBAAgB,EAAE,CAAC,eAAe,CAAC;SACpC;QACD;YACE,WAAW,EAAE,+DAA+D;YAC5E,QAAQ,EAAE,CAAC,uBAAuB,EAAE,gBAAgB,EAAE,WAAW,CAAC;YAClE,gBAAgB,EAAE,CAAC,gBAAgB,EAAE,cAAc,EAAE,mBAAmB,CAAC;SAC1E;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Allowlisting System
|
|
3
|
+
*
|
|
4
|
+
* Entry point for Halo's framework-aware rule management. When a developer
|
|
5
|
+
* declares their framework in .halorc.json (e.g. { "framework": "nextjs" }),
|
|
6
|
+
* Halo loads the corresponding profile and automatically suppresses or
|
|
7
|
+
* downgrades rules that the framework already handles natively.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { applyFrameworkOverrides } from './frameworks';
|
|
11
|
+
* const result = applyFrameworkOverrides(violations, 'nextjs');
|
|
12
|
+
* // result.violations — filtered/downgraded violations
|
|
13
|
+
* // result.suppressedCount — number of violations removed
|
|
14
|
+
* // result.downgradedCount — number of violations with reduced severity
|
|
15
|
+
*/
|
|
16
|
+
export type { FrameworkAction, FrameworkRuleOverride, FrameworkSafePattern, FrameworkProfile } from './types';
|
|
17
|
+
import { FrameworkProfile } from './types';
|
|
18
|
+
/**
|
|
19
|
+
* Minimal violation shape used by the framework system to avoid circular
|
|
20
|
+
* imports with the engine's Violation type. Any object with at least ruleId
|
|
21
|
+
* and severity will work.
|
|
22
|
+
*/
|
|
23
|
+
interface ViolationLike {
|
|
24
|
+
ruleId: string;
|
|
25
|
+
severity: string;
|
|
26
|
+
[key: string]: any;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Look up a framework profile by its id.
|
|
30
|
+
*
|
|
31
|
+
* @param id - Framework identifier (e.g. "nextjs", "django", "rails")
|
|
32
|
+
* @returns The matching FrameworkProfile, or null if not found
|
|
33
|
+
*/
|
|
34
|
+
export declare function getFrameworkProfile(id: string): FrameworkProfile | null;
|
|
35
|
+
/**
|
|
36
|
+
* List all registered framework ids.
|
|
37
|
+
*
|
|
38
|
+
* @returns Sorted array of framework id strings
|
|
39
|
+
*/
|
|
40
|
+
export declare function listFrameworks(): string[];
|
|
41
|
+
/**
|
|
42
|
+
* Apply a framework's rule overrides to a set of violations.
|
|
43
|
+
*
|
|
44
|
+
* For each violation whose ruleId appears in the framework's handled_rules:
|
|
45
|
+
* - "suppress" removes the violation from the output entirely
|
|
46
|
+
* - "downgrade" reduces the violation's severity to the specified level
|
|
47
|
+
*
|
|
48
|
+
* Violations whose ruleId is NOT in the framework's profile are passed
|
|
49
|
+
* through unchanged.
|
|
50
|
+
*
|
|
51
|
+
* @param violations - Array of violation objects to filter
|
|
52
|
+
* @param frameworkId - Framework identifier to look up
|
|
53
|
+
* @returns Object with the filtered violations array and counts
|
|
54
|
+
*/
|
|
55
|
+
export declare function applyFrameworkOverrides<T extends ViolationLike>(violations: T[], frameworkId: string): {
|
|
56
|
+
violations: T[];
|
|
57
|
+
suppressedCount: number;
|
|
58
|
+
downgradedCount: number;
|
|
59
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Framework Allowlisting System
|
|
4
|
+
*
|
|
5
|
+
* Entry point for Halo's framework-aware rule management. When a developer
|
|
6
|
+
* declares their framework in .halorc.json (e.g. { "framework": "nextjs" }),
|
|
7
|
+
* Halo loads the corresponding profile and automatically suppresses or
|
|
8
|
+
* downgrades rules that the framework already handles natively.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { applyFrameworkOverrides } from './frameworks';
|
|
12
|
+
* const result = applyFrameworkOverrides(violations, 'nextjs');
|
|
13
|
+
* // result.violations — filtered/downgraded violations
|
|
14
|
+
* // result.suppressedCount — number of violations removed
|
|
15
|
+
* // result.downgradedCount — number of violations with reduced severity
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.getFrameworkProfile = getFrameworkProfile;
|
|
19
|
+
exports.listFrameworks = listFrameworks;
|
|
20
|
+
exports.applyFrameworkOverrides = applyFrameworkOverrides;
|
|
21
|
+
const nextjs_1 = require("./nextjs");
|
|
22
|
+
const django_1 = require("./django");
|
|
23
|
+
const rails_1 = require("./rails");
|
|
24
|
+
/** Registry of all built-in framework profiles keyed by id. */
|
|
25
|
+
const FRAMEWORK_REGISTRY = {
|
|
26
|
+
nextjs: nextjs_1.nextjsProfile,
|
|
27
|
+
django: django_1.djangoProfile,
|
|
28
|
+
rails: rails_1.railsProfile,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Look up a framework profile by its id.
|
|
32
|
+
*
|
|
33
|
+
* @param id - Framework identifier (e.g. "nextjs", "django", "rails")
|
|
34
|
+
* @returns The matching FrameworkProfile, or null if not found
|
|
35
|
+
*/
|
|
36
|
+
function getFrameworkProfile(id) {
|
|
37
|
+
return FRAMEWORK_REGISTRY[id] ?? null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* List all registered framework ids.
|
|
41
|
+
*
|
|
42
|
+
* @returns Sorted array of framework id strings
|
|
43
|
+
*/
|
|
44
|
+
function listFrameworks() {
|
|
45
|
+
return Object.keys(FRAMEWORK_REGISTRY).sort();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Apply a framework's rule overrides to a set of violations.
|
|
49
|
+
*
|
|
50
|
+
* For each violation whose ruleId appears in the framework's handled_rules:
|
|
51
|
+
* - "suppress" removes the violation from the output entirely
|
|
52
|
+
* - "downgrade" reduces the violation's severity to the specified level
|
|
53
|
+
*
|
|
54
|
+
* Violations whose ruleId is NOT in the framework's profile are passed
|
|
55
|
+
* through unchanged.
|
|
56
|
+
*
|
|
57
|
+
* @param violations - Array of violation objects to filter
|
|
58
|
+
* @param frameworkId - Framework identifier to look up
|
|
59
|
+
* @returns Object with the filtered violations array and counts
|
|
60
|
+
*/
|
|
61
|
+
function applyFrameworkOverrides(violations, frameworkId) {
|
|
62
|
+
const profile = getFrameworkProfile(frameworkId);
|
|
63
|
+
if (!profile) {
|
|
64
|
+
return { violations: [...violations], suppressedCount: 0, downgradedCount: 0 };
|
|
65
|
+
}
|
|
66
|
+
// Build a lookup map from rule_id to override for O(1) access
|
|
67
|
+
const overrideMap = new Map(profile.handled_rules.map((override) => [override.rule_id, override]));
|
|
68
|
+
let suppressedCount = 0;
|
|
69
|
+
let downgradedCount = 0;
|
|
70
|
+
const filtered = [];
|
|
71
|
+
for (const violation of violations) {
|
|
72
|
+
const override = overrideMap.get(violation.ruleId);
|
|
73
|
+
if (!override) {
|
|
74
|
+
filtered.push(violation);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (override.action === 'suppress') {
|
|
78
|
+
suppressedCount++;
|
|
79
|
+
// Violation is removed from output
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (override.action === 'downgrade' && override.downgrade_to) {
|
|
83
|
+
downgradedCount++;
|
|
84
|
+
// Create a shallow copy with the new severity to avoid mutating the original
|
|
85
|
+
filtered.push({ ...violation, severity: override.downgrade_to });
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
// Fallback: pass through if action is unrecognized
|
|
89
|
+
filtered.push(violation);
|
|
90
|
+
}
|
|
91
|
+
return { violations: filtered, suppressedCount, downgradedCount };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/frameworks/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAiCH,kDAEC;AAOD,wCAEC;AAgBD,0DA6CC;AApGD,qCAAyC;AACzC,qCAAyC;AACzC,mCAAuC;AAavC,+DAA+D;AAC/D,MAAM,kBAAkB,GAAqC;IAC3D,MAAM,EAAE,sBAAa;IACrB,MAAM,EAAE,sBAAa;IACrB,KAAK,EAAE,oBAAY;CACpB,CAAC;AAEF;;;;;GAKG;AACH,SAAgB,mBAAmB,CAAC,EAAU;IAC5C,OAAO,kBAAkB,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc;IAC5B,OAAO,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,IAAI,EAAE,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,uBAAuB,CACrC,UAAe,EACf,WAAmB;IAEnB,MAAM,OAAO,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAEjD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;IACjF,CAAC;IAED,8DAA8D;IAC9D,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CACtE,CAAC;IAEF,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,MAAM,QAAQ,GAAQ,EAAE,CAAC;IAEzB,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAEnD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzB,SAAS;QACX,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACnC,eAAe,EAAE,CAAC;YAClB,mCAAmC;YACnC,SAAS;QACX,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,WAAW,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC7D,eAAe,EAAE,CAAC;YAClB,6EAA6E;YAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;YACjE,SAAS;QACX,CAAC;QAED,mDAAmD;QACnD,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,eAAe,EAAE,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Framework Profile
|
|
3
|
+
*
|
|
4
|
+
* Next.js provides built-in protections that overlap with several COPPA rules:
|
|
5
|
+
* - React JSX auto-escaping mitigates most XSS vectors
|
|
6
|
+
* - HTTPS enforcement in production covers unencrypted PII transmission
|
|
7
|
+
* - Next.js Link component handles external navigation safely
|
|
8
|
+
* - Middleware provides centralized cookie management
|
|
9
|
+
*/
|
|
10
|
+
import { FrameworkProfile } from './types';
|
|
11
|
+
export declare const nextjsProfile: FrameworkProfile;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Next.js Framework Profile
|
|
4
|
+
*
|
|
5
|
+
* Next.js provides built-in protections that overlap with several COPPA rules:
|
|
6
|
+
* - React JSX auto-escaping mitigates most XSS vectors
|
|
7
|
+
* - HTTPS enforcement in production covers unencrypted PII transmission
|
|
8
|
+
* - Next.js Link component handles external navigation safely
|
|
9
|
+
* - Middleware provides centralized cookie management
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.nextjsProfile = void 0;
|
|
13
|
+
exports.nextjsProfile = {
|
|
14
|
+
id: 'nextjs',
|
|
15
|
+
name: 'Next.js',
|
|
16
|
+
ecosystem: 'javascript',
|
|
17
|
+
handled_rules: [
|
|
18
|
+
{
|
|
19
|
+
rule_id: 'coppa-sec-015',
|
|
20
|
+
action: 'downgrade',
|
|
21
|
+
downgrade_to: 'low',
|
|
22
|
+
reason: 'React auto-escapes JSX output by default. dangerouslySetInnerHTML is the only XSS vector and is flagged separately.',
|
|
23
|
+
documentation_url: 'https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
rule_id: 'coppa-sec-006',
|
|
27
|
+
action: 'suppress',
|
|
28
|
+
reason: 'Next.js enforces HTTPS in production. API routes run server-side. Development HTTP is expected.',
|
|
29
|
+
documentation_url: 'https://nextjs.org/docs/app/api-reference/next-config-js/headers',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
rule_id: 'coppa-ext-017',
|
|
33
|
+
action: 'downgrade',
|
|
34
|
+
downgrade_to: 'low',
|
|
35
|
+
reason: "Next.js Link component handles external navigation. rel='noopener noreferrer' is auto-added.",
|
|
36
|
+
documentation_url: 'https://nextjs.org/docs/app/api-reference/components/link',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
rule_id: 'coppa-cookies-016',
|
|
40
|
+
action: 'downgrade',
|
|
41
|
+
downgrade_to: 'low',
|
|
42
|
+
reason: 'Next.js middleware can intercept and manage cookies centrally.',
|
|
43
|
+
documentation_url: 'https://nextjs.org/docs/app/building-your-application/routing/middleware',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
safe_patterns: [
|
|
47
|
+
{
|
|
48
|
+
description: 'Next.js Image component for optimized, safe image handling',
|
|
49
|
+
patterns: [/next\/image/, /<Image\s/],
|
|
50
|
+
applies_to_rules: ['coppa-ugc-014'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
description: 'Next.js middleware for centralized request/response interception',
|
|
54
|
+
patterns: [/middleware\.(ts|js)/, /NextResponse/],
|
|
55
|
+
applies_to_rules: ['coppa-sec-015'],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=nextjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/frameworks/nextjs.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAIU,QAAA,aAAa,GAAqB;IAC7C,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,YAAY;IACvB,aAAa,EAAE;QACb;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,WAAW;YACnB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,qHAAqH;YAC7H,iBAAiB,EAAE,4FAA4F;SAChH;QACD;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,iGAAiG;YACzG,iBAAiB,EAAE,kEAAkE;SACtF;QACD;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,WAAW;YACnB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,8FAA8F;YACtG,iBAAiB,EAAE,2DAA2D;SAC/E;QACD;YACE,OAAO,EAAE,mBAAmB;YAC5B,MAAM,EAAE,WAAW;YACnB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,gEAAgE;YACxE,iBAAiB,EAAE,0EAA0E;SAC9F;KACF;IACD,aAAa,EAAE;QACb;YACE,WAAW,EAAE,4DAA4D;YACzE,QAAQ,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC;YACrC,gBAAgB,EAAE,CAAC,eAAe,CAAC;SACpC;QACD;YACE,WAAW,EAAE,kEAAkE;YAC/E,QAAQ,EAAE,CAAC,qBAAqB,EAAE,cAAc,CAAC;YACjD,gBAAgB,EAAE,CAAC,eAAe,CAAC;SACpC;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ruby on Rails Framework Profile
|
|
3
|
+
*
|
|
4
|
+
* Rails provides built-in protections that overlap with several COPPA rules:
|
|
5
|
+
* - ERB templates auto-escape all output by default
|
|
6
|
+
* - Strong parameters filter mass assignment (mitigates PII leaks)
|
|
7
|
+
* - ActiveRecord supports soft delete via acts_as_paranoid or discard gem
|
|
8
|
+
* - force_ssl config enforces HTTPS application-wide
|
|
9
|
+
*/
|
|
10
|
+
import { FrameworkProfile } from './types';
|
|
11
|
+
export declare const railsProfile: FrameworkProfile;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Ruby on Rails Framework Profile
|
|
4
|
+
*
|
|
5
|
+
* Rails provides built-in protections that overlap with several COPPA rules:
|
|
6
|
+
* - ERB templates auto-escape all output by default
|
|
7
|
+
* - Strong parameters filter mass assignment (mitigates PII leaks)
|
|
8
|
+
* - ActiveRecord supports soft delete via acts_as_paranoid or discard gem
|
|
9
|
+
* - force_ssl config enforces HTTPS application-wide
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.railsProfile = void 0;
|
|
13
|
+
exports.railsProfile = {
|
|
14
|
+
id: 'rails',
|
|
15
|
+
name: 'Ruby on Rails',
|
|
16
|
+
ecosystem: 'ruby',
|
|
17
|
+
handled_rules: [
|
|
18
|
+
{
|
|
19
|
+
rule_id: 'coppa-sec-015',
|
|
20
|
+
action: 'suppress',
|
|
21
|
+
reason: 'ERB templates auto-escape all output by default.',
|
|
22
|
+
documentation_url: 'https://guides.rubyonrails.org/security.html#cross-site-scripting-xss',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
rule_id: 'coppa-data-002',
|
|
26
|
+
action: 'downgrade',
|
|
27
|
+
downgrade_to: 'low',
|
|
28
|
+
reason: 'Rails strong parameters filter mass assignment.',
|
|
29
|
+
documentation_url: 'https://guides.rubyonrails.org/action_controller_overview.html#strong-parameters',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
rule_id: 'coppa-retention-005',
|
|
33
|
+
action: 'downgrade',
|
|
34
|
+
downgrade_to: 'low',
|
|
35
|
+
reason: 'ActiveRecord supports soft delete via acts_as_paranoid or discard gem.',
|
|
36
|
+
documentation_url: 'https://github.com/jhawthorn/discard',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
rule_id: 'coppa-sec-006',
|
|
40
|
+
action: 'suppress',
|
|
41
|
+
reason: 'Rails force_ssl config enforces HTTPS.',
|
|
42
|
+
documentation_url: 'https://guides.rubyonrails.org/configuring.html#config-force-ssl',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
safe_patterns: [
|
|
46
|
+
{
|
|
47
|
+
description: 'Rails CSRF protection via protect_from_forgery and authenticity tokens',
|
|
48
|
+
patterns: [/protect_from_forgery/, /authenticity_token/],
|
|
49
|
+
applies_to_rules: ['coppa-sec-015'],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
description: 'Rails ActiveRecord encryption for sensitive attributes',
|
|
53
|
+
patterns: [/encrypts\s+:/, /has_encrypted/],
|
|
54
|
+
applies_to_rules: ['coppa-sec-006'],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=rails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rails.js","sourceRoot":"","sources":["../../src/frameworks/rails.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAIU,QAAA,YAAY,GAAqB;IAC5C,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,eAAe;IACrB,SAAS,EAAE,MAAM;IACjB,aAAa,EAAE;QACb;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,kDAAkD;YAC1D,iBAAiB,EAAE,uEAAuE;SAC3F;QACD;YACE,OAAO,EAAE,gBAAgB;YACzB,MAAM,EAAE,WAAW;YACnB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,iDAAiD;YACzD,iBAAiB,EAAE,kFAAkF;SACtG;QACD;YACE,OAAO,EAAE,qBAAqB;YAC9B,MAAM,EAAE,WAAW;YACnB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,wEAAwE;YAChF,iBAAiB,EAAE,sCAAsC;SAC1D;QACD;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,wCAAwC;YAChD,iBAAiB,EAAE,kEAAkE;SACtF;KACF;IACD,aAAa,EAAE;QACb;YACE,WAAW,EAAE,wEAAwE;YACrF,QAAQ,EAAE,CAAC,sBAAsB,EAAE,oBAAoB,CAAC;YACxD,gBAAgB,EAAE,CAAC,eAAe,CAAC;SACpC;QACD;YACE,WAAW,EAAE,wDAAwD;YACrE,QAAQ,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;YAC3C,gBAAgB,EAAE,CAAC,eAAe,CAAC;SACpC;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Allowlisting Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Defines the type contracts for framework profiles that declare which
|
|
5
|
+
* COPPA/ethical rules a framework already handles natively. When a developer
|
|
6
|
+
* declares their framework in .halorc.json, Halo uses these profiles to
|
|
7
|
+
* suppress or downgrade rules the framework covers automatically.
|
|
8
|
+
*/
|
|
9
|
+
export type FrameworkAction = 'suppress' | 'downgrade';
|
|
10
|
+
export interface FrameworkRuleOverride {
|
|
11
|
+
rule_id: string;
|
|
12
|
+
action: FrameworkAction;
|
|
13
|
+
downgrade_to?: 'critical' | 'high' | 'medium' | 'low';
|
|
14
|
+
condition?: string;
|
|
15
|
+
reason: string;
|
|
16
|
+
documentation_url?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface FrameworkSafePattern {
|
|
19
|
+
description: string;
|
|
20
|
+
patterns: RegExp[];
|
|
21
|
+
applies_to_rules: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface FrameworkProfile {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
ecosystem: 'javascript' | 'python' | 'ruby';
|
|
27
|
+
handled_rules: FrameworkRuleOverride[];
|
|
28
|
+
safe_patterns: FrameworkSafePattern[];
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Framework Allowlisting Type Definitions
|
|
4
|
+
*
|
|
5
|
+
* Defines the type contracts for framework profiles that declare which
|
|
6
|
+
* COPPA/ethical rules a framework already handles natively. When a developer
|
|
7
|
+
* declares their framework in .halorc.json, Halo uses these profiles to
|
|
8
|
+
* suppress or downgrade rules the framework covers automatically.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/frameworks/types.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
* Sprint 1 Fixes: Added tree-sitter for AST analysis, YAML rule loading
|
|
8
8
|
*/
|
|
9
9
|
import Parser from 'tree-sitter';
|
|
10
|
+
import { ConfidenceResult, ConfidenceSignals, ConfidenceInterpretation, ViolationInput } from './context-analyzer';
|
|
11
|
+
export type { ConfidenceResult, ConfidenceSignals, ConfidenceInterpretation, ViolationInput };
|
|
10
12
|
export type Severity = 'critical' | 'high' | 'medium' | 'low';
|
|
13
|
+
export type ASTVerdict = 'confirmed' | 'suppressed' | 'regex_only';
|
|
11
14
|
export type Fixability = 'auto' | 'guided' | 'flag-only';
|
|
12
15
|
export interface RemediationSpec {
|
|
13
16
|
fixability: Fixability;
|
|
@@ -34,6 +37,20 @@ export interface Violation {
|
|
|
34
37
|
matchType?: 'regex' | 'ast' | 'hybrid';
|
|
35
38
|
fixability?: Fixability;
|
|
36
39
|
remediation?: RemediationSpec;
|
|
40
|
+
/** AST analysis verdict: confirmed, suppressed, or regex_only */
|
|
41
|
+
astVerdict?: ASTVerdict;
|
|
42
|
+
/** AST analysis confidence: 0.0 to 1.0 */
|
|
43
|
+
astConfidence?: number;
|
|
44
|
+
/** Reason for AST verdict */
|
|
45
|
+
astReason?: string;
|
|
46
|
+
/** Whether this violation was suppressed by framework profile */
|
|
47
|
+
frameworkSuppressed?: boolean;
|
|
48
|
+
/** ContextAnalyzer confidence score (0.0-1.0) */
|
|
49
|
+
confidence?: number;
|
|
50
|
+
/** ContextAnalyzer interpretation */
|
|
51
|
+
confidenceInterpretation?: ConfidenceInterpretation;
|
|
52
|
+
/** ContextAnalyzer reason string */
|
|
53
|
+
confidenceReason?: string;
|
|
37
54
|
}
|
|
38
55
|
export interface Rule {
|
|
39
56
|
id: string;
|
|
@@ -51,6 +68,43 @@ export interface SuppressionConfig {
|
|
|
51
68
|
commentPattern: string;
|
|
52
69
|
}
|
|
53
70
|
export declare function loadRulesFromYAML(yamlPath: string): Rule[];
|
|
71
|
+
export interface JSONRuleConfig {
|
|
72
|
+
version: string;
|
|
73
|
+
generated_at: string;
|
|
74
|
+
packs: Record<string, {
|
|
75
|
+
id: string;
|
|
76
|
+
name: string;
|
|
77
|
+
description: string;
|
|
78
|
+
jurisdiction: string;
|
|
79
|
+
jurisdiction_level: string;
|
|
80
|
+
is_free: boolean;
|
|
81
|
+
effective_date: string | null;
|
|
82
|
+
source_url: string;
|
|
83
|
+
}>;
|
|
84
|
+
rules: JSONRule[];
|
|
85
|
+
}
|
|
86
|
+
export interface JSONRule {
|
|
87
|
+
id: string;
|
|
88
|
+
name: string;
|
|
89
|
+
severity: Severity;
|
|
90
|
+
category: string;
|
|
91
|
+
description: string;
|
|
92
|
+
patterns: Array<{
|
|
93
|
+
pattern: string;
|
|
94
|
+
flags: string;
|
|
95
|
+
}>;
|
|
96
|
+
fix_suggestion: string;
|
|
97
|
+
penalty: string;
|
|
98
|
+
languages: string[];
|
|
99
|
+
packs: string[];
|
|
100
|
+
fixability: string;
|
|
101
|
+
transform_type: string | null;
|
|
102
|
+
scaffold_id: string | null;
|
|
103
|
+
guidance_url: string | null;
|
|
104
|
+
}
|
|
105
|
+
export declare function loadRulesFromJSON(jsonPath: string): Rule[];
|
|
106
|
+
export declare function loadRulesFromJSONByPack(jsonPath: string, packIds: string[]): Rule[];
|
|
107
|
+
export declare function compileRawRules(rawRules: JSONRule[]): Rule[];
|
|
54
108
|
export declare class TreeSitterParser {
|
|
55
109
|
private parser;
|
|
56
110
|
constructor();
|
|
@@ -124,6 +178,13 @@ export interface EngineConfig {
|
|
|
124
178
|
ethical?: boolean;
|
|
125
179
|
aiAudit?: boolean;
|
|
126
180
|
sectorAuSbd?: boolean;
|
|
181
|
+
sectorAuOsa?: boolean;
|
|
182
|
+
packs?: string[];
|
|
183
|
+
loadedRules?: Rule[];
|
|
184
|
+
framework?: string;
|
|
185
|
+
astAnalysis?: boolean;
|
|
186
|
+
historicalFPRates?: Record<string, number>;
|
|
187
|
+
suppressionRates?: Record<string, number>;
|
|
127
188
|
}
|
|
128
189
|
export interface ScanResult {
|
|
129
190
|
filePath: string;
|
|
@@ -137,7 +198,20 @@ export declare class HaloEngine {
|
|
|
137
198
|
private config;
|
|
138
199
|
private rules;
|
|
139
200
|
private treeSitter;
|
|
201
|
+
private astEngine;
|
|
202
|
+
private contextAnalyzer;
|
|
140
203
|
constructor(config?: EngineConfig);
|
|
204
|
+
/**
|
|
205
|
+
* Load rules from the bundled rules.json file, filtered by pack IDs.
|
|
206
|
+
* Falls back to empty array if rules.json not found.
|
|
207
|
+
*/
|
|
208
|
+
private loadBundledRulesByPack;
|
|
209
|
+
/**
|
|
210
|
+
* Resolve legacy boolean flags to pack IDs.
|
|
211
|
+
* Maps ethical/aiAudit/sectorAuSbd booleans to their pack ID equivalents.
|
|
212
|
+
* Always includes 'coppa' as the base pack.
|
|
213
|
+
*/
|
|
214
|
+
static resolvePacks(config: EngineConfig): string[];
|
|
141
215
|
/**
|
|
142
216
|
* Get the tree-sitter parser for advanced AST analysis
|
|
143
217
|
*/
|