@runhalo/engine 0.7.0 → 0.9.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/coppa-countdown.d.ts +44 -0
- package/dist/coppa-countdown.js +107 -0
- package/dist/coppa-countdown.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +135 -103
- package/dist/index.js.map +1 -1
- package/dist/sdk-intelligence.d.ts +37 -0
- package/dist/sdk-intelligence.js +246 -0
- package/dist/sdk-intelligence.js.map +1 -0
- package/dist/tier-context.d.ts +84 -0
- package/dist/tier-context.js +241 -0
- package/dist/tier-context.js.map +1 -0
- package/package.json +1 -1
- package/rules/rules.json +4366 -1059
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COPPA 2.0 Countdown Utility
|
|
3
|
+
*
|
|
4
|
+
* Sprint 19: Shared across all output surfaces (CLI, GitHub Action, PDF, dashboard).
|
|
5
|
+
* COPPA 2.0 enforcement begins April 22, 2026.
|
|
6
|
+
* Maximum penalty: $53,088 per violation per day.
|
|
7
|
+
*
|
|
8
|
+
* Don't Go Backwards Rule: Every customer touchpoint includes this countdown.
|
|
9
|
+
*/
|
|
10
|
+
declare const COPPA_2_ENFORCEMENT_DATE: Date;
|
|
11
|
+
declare const PENALTY_PER_VIOLATION_PER_DAY = 53088;
|
|
12
|
+
export interface CoppaCountdown {
|
|
13
|
+
/** Days until enforcement (0 if already active) */
|
|
14
|
+
days: number;
|
|
15
|
+
/** Human-readable message */
|
|
16
|
+
message: string;
|
|
17
|
+
/** Whether COPPA 2.0 is currently in effect */
|
|
18
|
+
isActive: boolean;
|
|
19
|
+
/** The enforcement date */
|
|
20
|
+
enforcementDate: Date;
|
|
21
|
+
/** Maximum penalty per violation per day */
|
|
22
|
+
penaltyPerDay: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the current COPPA 2.0 countdown status.
|
|
26
|
+
* Used by every output surface in Halo.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getCoppaCountdown(now?: Date): CoppaCountdown;
|
|
29
|
+
/**
|
|
30
|
+
* Format the COPPA countdown for CLI output (with ANSI colors).
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatCoppaCountdownCLI(countdown?: CoppaCountdown): string;
|
|
33
|
+
/**
|
|
34
|
+
* Format the COPPA countdown for GitHub Action PR comments (Markdown).
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatCoppaCountdownMarkdown(countdown?: CoppaCountdown): string;
|
|
37
|
+
/**
|
|
38
|
+
* Format for PDF report headers.
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatCoppaCountdownPDF(countdown?: CoppaCountdown): {
|
|
41
|
+
text: string;
|
|
42
|
+
severity: 'urgent' | 'warning' | 'active';
|
|
43
|
+
};
|
|
44
|
+
export { COPPA_2_ENFORCEMENT_DATE, PENALTY_PER_VIOLATION_PER_DAY };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* COPPA 2.0 Countdown Utility
|
|
4
|
+
*
|
|
5
|
+
* Sprint 19: Shared across all output surfaces (CLI, GitHub Action, PDF, dashboard).
|
|
6
|
+
* COPPA 2.0 enforcement begins April 22, 2026.
|
|
7
|
+
* Maximum penalty: $53,088 per violation per day.
|
|
8
|
+
*
|
|
9
|
+
* Don't Go Backwards Rule: Every customer touchpoint includes this countdown.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PENALTY_PER_VIOLATION_PER_DAY = exports.COPPA_2_ENFORCEMENT_DATE = void 0;
|
|
13
|
+
exports.getCoppaCountdown = getCoppaCountdown;
|
|
14
|
+
exports.formatCoppaCountdownCLI = formatCoppaCountdownCLI;
|
|
15
|
+
exports.formatCoppaCountdownMarkdown = formatCoppaCountdownMarkdown;
|
|
16
|
+
exports.formatCoppaCountdownPDF = formatCoppaCountdownPDF;
|
|
17
|
+
const COPPA_2_ENFORCEMENT_DATE = new Date('2026-04-22T00:00:00Z');
|
|
18
|
+
exports.COPPA_2_ENFORCEMENT_DATE = COPPA_2_ENFORCEMENT_DATE;
|
|
19
|
+
const PENALTY_PER_VIOLATION_PER_DAY = 53088;
|
|
20
|
+
exports.PENALTY_PER_VIOLATION_PER_DAY = PENALTY_PER_VIOLATION_PER_DAY;
|
|
21
|
+
/**
|
|
22
|
+
* Get the current COPPA 2.0 countdown status.
|
|
23
|
+
* Used by every output surface in Halo.
|
|
24
|
+
*/
|
|
25
|
+
function getCoppaCountdown(now) {
|
|
26
|
+
const currentDate = now || new Date();
|
|
27
|
+
const diffMs = COPPA_2_ENFORCEMENT_DATE.getTime() - currentDate.getTime();
|
|
28
|
+
const days = Math.max(0, Math.ceil(diffMs / 86400000));
|
|
29
|
+
if (days > 0) {
|
|
30
|
+
return {
|
|
31
|
+
days,
|
|
32
|
+
message: `COPPA 2.0 enforcement begins April 22, 2026 \u2014 ${days} day${days === 1 ? '' : 's'}`,
|
|
33
|
+
isActive: false,
|
|
34
|
+
enforcementDate: COPPA_2_ENFORCEMENT_DATE,
|
|
35
|
+
penaltyPerDay: PENALTY_PER_VIOLATION_PER_DAY,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
days: 0,
|
|
40
|
+
message: 'COPPA 2.0 is NOW IN EFFECT',
|
|
41
|
+
isActive: true,
|
|
42
|
+
enforcementDate: COPPA_2_ENFORCEMENT_DATE,
|
|
43
|
+
penaltyPerDay: PENALTY_PER_VIOLATION_PER_DAY,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Format the COPPA countdown for CLI output (with ANSI colors).
|
|
48
|
+
*/
|
|
49
|
+
function formatCoppaCountdownCLI(countdown) {
|
|
50
|
+
const cd = countdown || getCoppaCountdown();
|
|
51
|
+
const lines = [
|
|
52
|
+
'\u2500'.repeat(55),
|
|
53
|
+
];
|
|
54
|
+
if (cd.isActive) {
|
|
55
|
+
lines.push(`\x1b[31m\x1b[1m\u26a0\ufe0f COPPA 2.0 is NOW IN EFFECT\x1b[0m`);
|
|
56
|
+
lines.push(` Maximum penalty: $${cd.penaltyPerDay.toLocaleString()}/violation/day`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
lines.push(`\x1b[33m\u26a0\ufe0f ${cd.message}\x1b[0m`);
|
|
60
|
+
lines.push(` Maximum penalty: $${cd.penaltyPerDay.toLocaleString()}/violation/day`);
|
|
61
|
+
}
|
|
62
|
+
lines.push('\u2500'.repeat(55));
|
|
63
|
+
return lines.join('\n');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Format the COPPA countdown for GitHub Action PR comments (Markdown).
|
|
67
|
+
*/
|
|
68
|
+
function formatCoppaCountdownMarkdown(countdown) {
|
|
69
|
+
const cd = countdown || getCoppaCountdown();
|
|
70
|
+
if (cd.isActive) {
|
|
71
|
+
return [
|
|
72
|
+
'---',
|
|
73
|
+
`> \u26a0\ufe0f **COPPA 2.0 is NOW IN EFFECT**`,
|
|
74
|
+
`> Maximum penalty: $${cd.penaltyPerDay.toLocaleString()} per violation per day`,
|
|
75
|
+
`> Run \`npx @runhalo/cli scan . --review\` for AI-verified compliance check`,
|
|
76
|
+
].join('\n');
|
|
77
|
+
}
|
|
78
|
+
return [
|
|
79
|
+
'---',
|
|
80
|
+
`> \u26a0\ufe0f **COPPA 2.0 enforcement begins April 22, 2026 \u2014 ${cd.days} day${cd.days === 1 ? '' : 's'}**`,
|
|
81
|
+
`> Maximum penalty: $${cd.penaltyPerDay.toLocaleString()} per violation per day`,
|
|
82
|
+
`> Run \`npx @runhalo/cli scan . --review\` for AI-verified compliance check`,
|
|
83
|
+
].join('\n');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Format for PDF report headers.
|
|
87
|
+
*/
|
|
88
|
+
function formatCoppaCountdownPDF(countdown) {
|
|
89
|
+
const cd = countdown || getCoppaCountdown();
|
|
90
|
+
if (cd.isActive) {
|
|
91
|
+
return {
|
|
92
|
+
text: 'COPPA 2.0 IS NOW IN EFFECT — Ensure ongoing compliance',
|
|
93
|
+
severity: 'active',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (cd.days <= 30) {
|
|
97
|
+
return {
|
|
98
|
+
text: `COPPA 2.0 ENFORCEMENT IN ${cd.days} DAYS — April 22, 2026`,
|
|
99
|
+
severity: 'urgent',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
text: `COPPA 2.0 enforcement begins April 22, 2026 (${cd.days} days)`,
|
|
104
|
+
severity: 'warning',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=coppa-countdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coppa-countdown.js","sourceRoot":"","sources":["../src/coppa-countdown.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -167,6 +167,7 @@ export interface JSONRule {
|
|
|
167
167
|
guidance_url: string | null;
|
|
168
168
|
}
|
|
169
169
|
export declare function loadRulesFromJSON(jsonPath: string): Rule[];
|
|
170
|
+
export declare const ALL_PACK_IDS: string[];
|
|
170
171
|
export declare function loadRulesFromJSONByPack(jsonPath: string, packIds: string[]): Rule[];
|
|
171
172
|
export declare function compileRawRules(rawRules: JSONRule[]): Rule[];
|
|
172
173
|
export declare class TreeSitterParser {
|
|
@@ -282,9 +283,9 @@ export declare class HaloEngine {
|
|
|
282
283
|
*/
|
|
283
284
|
private loadBundledRulesByPack;
|
|
284
285
|
/**
|
|
285
|
-
* Resolve
|
|
286
|
-
*
|
|
287
|
-
*
|
|
286
|
+
* Resolve pack IDs from config.
|
|
287
|
+
* Halo 2.0: defaults to ALL packs (loose pre-filter for AI Review Board).
|
|
288
|
+
* Legacy boolean flags still supported for backward compatibility.
|
|
288
289
|
*/
|
|
289
290
|
static resolvePacks(config: EngineConfig): string[];
|
|
290
291
|
/**
|
|
@@ -332,4 +333,10 @@ export { detectFramework } from './framework-detect';
|
|
|
332
333
|
export type { Framework, FrameworkDetectionResult } from './framework-detect';
|
|
333
334
|
export { SCAFFOLD_REGISTRY } from './scaffolds/index';
|
|
334
335
|
export type { ScaffoldTemplate, ScaffoldFile } from './scaffolds/index';
|
|
336
|
+
export { detectSDKsFromPackageJson, generateSDKContext, detectSDKs, SDK_RISK_DATABASE } from './sdk-intelligence';
|
|
337
|
+
export type { SDKRiskProfile, DetectedSDK } from './sdk-intelligence';
|
|
338
|
+
export { createTierContext, resolveTierFromKey, getUpgradeCTA, getTierComparison, TIER_LIMITS, TIER_FEATURES, TIER_DISPLAY_NAMES } from './tier-context';
|
|
339
|
+
export type { HaloTier, TierLimits, TierFeatures, TierContext } from './tier-context';
|
|
340
|
+
export { getCoppaCountdown, formatCoppaCountdownCLI, formatCoppaCountdownMarkdown, formatCoppaCountdownPDF, COPPA_2_ENFORCEMENT_DATE, PENALTY_PER_VIOLATION_PER_DAY } from './coppa-countdown';
|
|
341
|
+
export type { CoppaCountdown } from './coppa-countdown';
|
|
335
342
|
export default HaloEngine;
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
44
44
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
45
45
|
};
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
-
exports.SCAFFOLD_REGISTRY = exports.detectFramework = exports.ScaffoldEngine = exports.ComplianceScoreEngine = exports.transformSetDefault = exports.transformSanitizeInput = exports.transformRemoveDefault = exports.transformUrlUpgrade = exports.FixEngine = exports.REMEDIATION_MAP = exports.HaloEngine = exports.AU_SBD_RULES = exports.AI_AUDIT_RULES = exports.ETHICAL_RULES = exports.COPPA_RULES = exports.treeSitterParser = exports.TreeSitterParser = void 0;
|
|
47
|
+
exports.PENALTY_PER_VIOLATION_PER_DAY = exports.COPPA_2_ENFORCEMENT_DATE = exports.formatCoppaCountdownPDF = exports.formatCoppaCountdownMarkdown = exports.formatCoppaCountdownCLI = exports.getCoppaCountdown = exports.TIER_DISPLAY_NAMES = exports.TIER_FEATURES = exports.TIER_LIMITS = exports.getTierComparison = exports.getUpgradeCTA = exports.resolveTierFromKey = exports.createTierContext = exports.SDK_RISK_DATABASE = exports.detectSDKs = exports.generateSDKContext = exports.detectSDKsFromPackageJson = exports.SCAFFOLD_REGISTRY = exports.detectFramework = exports.ScaffoldEngine = exports.ComplianceScoreEngine = exports.transformSetDefault = exports.transformSanitizeInput = exports.transformRemoveDefault = exports.transformUrlUpgrade = exports.FixEngine = exports.REMEDIATION_MAP = exports.HaloEngine = exports.AU_SBD_RULES = exports.AI_AUDIT_RULES = exports.ETHICAL_RULES = exports.COPPA_RULES = exports.treeSitterParser = exports.TreeSitterParser = exports.ALL_PACK_IDS = void 0;
|
|
48
48
|
exports.classifyFile = classifyFile;
|
|
49
49
|
exports.loadRulesFromYAML = loadRulesFromYAML;
|
|
50
50
|
exports.loadRulesFromJSON = loadRulesFromJSON;
|
|
@@ -313,6 +313,13 @@ function loadRulesFromJSON(jsonPath) {
|
|
|
313
313
|
return [];
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
|
+
// ── Halo 2.0: All 16 packs active by default ──
|
|
317
|
+
// Every pack fires as a loose pre-filter. AI Review Board handles precision.
|
|
318
|
+
exports.ALL_PACK_IDS = [
|
|
319
|
+
'coppa', 'ethical', 'ai-audit', 'au-sbd', 'ut-sb142', 'uk-aadc',
|
|
320
|
+
'eu-dsa', 'au-osa', 'caadca', 'eu-ai-act', 'gdpr-art8', 'india-dpdp',
|
|
321
|
+
'brazil-lgpd', 'canada-pipeda', 'south-korea-pipa', 'behavioral-design',
|
|
322
|
+
];
|
|
316
323
|
// JSON Rule Loader — Load rules filtered by pack IDs
|
|
317
324
|
function loadRulesFromJSONByPack(jsonPath, packIds) {
|
|
318
325
|
try {
|
|
@@ -650,18 +657,18 @@ exports.COPPA_RULES = [
|
|
|
650
657
|
severity: 'critical',
|
|
651
658
|
description: 'HTTP transmission of PII exposes data in transit. All API endpoints handling personal information must use HTTPS.',
|
|
652
659
|
patterns: [
|
|
653
|
-
/http:\/\/[^\s]*(\/api\/|\/login|\/user|\/register|\/profile)/gi,
|
|
654
|
-
/http:\/\/localhost:[^\s]*(\/api\/)/gi,
|
|
655
|
-
/axios\.get\s*\(\s*['"]http:\/\//gi,
|
|
656
|
-
/fetch\s*\(\s*['"]http:\/\//gi,
|
|
657
|
-
/http:\/\/[^\s]*email[^\s]*/gi,
|
|
660
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))http:\/\/[^\s]*(\/api\/|\/login|\/user|\/register|\/profile)/gi,
|
|
661
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))http:\/\/localhost:[^\s]*(\/api\/)/gi,
|
|
662
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))axios\.get\s*\(\s*['"]http:\/\//gi,
|
|
663
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))fetch\s*\(\s*['"]http:\/\//gi,
|
|
664
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))http:\/\/[^\s]*email[^\s]*/gi,
|
|
658
665
|
// Python — requests/urllib with HTTP
|
|
659
|
-
/requests\.(?:get|post)\s*\(\s*['"]http:\/\/(?!localhost)/gi,
|
|
660
|
-
/urllib\.request\.urlopen\s*\(\s*['"]http:\/\/(?!localhost)/gi,
|
|
666
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))requests\.(?:get|post)\s*\(\s*['"]http:\/\/(?!localhost)/gi,
|
|
667
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))urllib\.request\.urlopen\s*\(\s*['"]http:\/\/(?!localhost)/gi,
|
|
661
668
|
// PHP — HTTP API calls
|
|
662
|
-
/(?:curl_setopt|file_get_contents|wp_remote_get)\s*\([^)]*['"]http:\/\/(?!localhost)/gi,
|
|
669
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))(?:curl_setopt|file_get_contents|wp_remote_get)\s*\([^)]*['"]http:\/\/(?!localhost)/gi,
|
|
663
670
|
// Ruby — HTTP requests
|
|
664
|
-
/(?:Net::HTTP|HTTParty|Faraday)\.(?:get|post)\s*\([^)]*['"]http:\/\/(?!localhost)/gi
|
|
671
|
+
/(?!.*(?:schemas\.|w3\.org|xmlns\.|\.config|README|example\.com|localhost|127\.0\.0|\.test|\.example|\.invalid|\.local|specification|documentation|comment|\/\/\s))(?:Net::HTTP|HTTParty|Faraday)\.(?:get|post)\s*\([^)]*['"]http:\/\/(?!localhost)/gi
|
|
665
672
|
],
|
|
666
673
|
fixSuggestion: 'Replace http:// with https:// for all API endpoints and resources',
|
|
667
674
|
penalty: 'Security breach liability + COPPA penalties',
|
|
@@ -705,17 +712,17 @@ exports.COPPA_RULES = [
|
|
|
705
712
|
patterns: [
|
|
706
713
|
// PascalCase component names: SignUpForm, RegisterForm, RegistrationForm, CreateAccountForm
|
|
707
714
|
// \b after Form prevents matching registerFormat, registerFormats, etc.
|
|
708
|
-
|
|
715
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|oauth|OAuth|lti|LTI|saml|SAML|sso|SSO|staff|moderator|enrollment|grading|assignment|syllabus|cartridge|yui|superclass|brickfield|sitepolicy|confirmation|FormattedMessage))\b(?:SignUp|Register|Registration|CreateAccount)Form\b/gi,
|
|
709
716
|
// kebab-case / snake_case: sign-up-form, register_form, create-account-form
|
|
710
|
-
|
|
717
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|oauth|OAuth|lti|LTI|saml|SAML|sso|SSO|staff|moderator|enrollment|grading|assignment|syllabus|cartridge|yui|superclass|brickfield|sitepolicy|confirmation|FormattedMessage))\b(?:sign[-_]?up|register|registration|create[-_]?account)[-_]form\b/gi,
|
|
711
718
|
// HTML form elements with registration-related ids/classes
|
|
712
|
-
|
|
719
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|oauth|OAuth|lti|LTI|saml|SAML|sso|SSO|staff|moderator|enrollment|grading|assignment|syllabus|cartridge|yui|superclass|brickfield|sitepolicy|confirmation|FormattedMessage))<form[^>]*(?:id|class|name)\s*=\s*["'][^"']*(?:register|signup|sign[-_]up|create[-_]account)[^"']*["']/gi,
|
|
713
720
|
// Python — Django/Flask registration form classes
|
|
714
|
-
/class\s+(?:SignUp|Register|Registration|CreateAccount)Form\s*\(\s*(?:forms\.Form|ModelForm|FlaskForm)/gi,
|
|
721
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|oauth|OAuth|lti|LTI|saml|SAML|sso|SSO|staff|moderator|enrollment|grading|assignment|syllabus|cartridge|yui|superclass|brickfield|sitepolicy|confirmation|FormattedMessage))class\s+(?:SignUp|Register|Registration|CreateAccount)Form\s*\(\s*(?:forms\.Form|ModelForm|FlaskForm)/gi,
|
|
715
722
|
// Ruby — Rails registration routes/controllers
|
|
716
|
-
/def\s+(?:sign_up|register|create_account)\b/gi,
|
|
723
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|oauth|OAuth|lti|LTI|saml|SAML|sso|SSO|staff|moderator|enrollment|grading|assignment|syllabus|cartridge|yui|superclass|brickfield|sitepolicy|confirmation|FormattedMessage))def\s+(?:sign_up|register|create_account)\b/gi,
|
|
717
724
|
// PHP — WordPress registration hooks
|
|
718
|
-
/(?:register_new_user|wp_create_user|user_register)\s*\(/gi
|
|
725
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|oauth|OAuth|lti|LTI|saml|SAML|sso|SSO|staff|moderator|enrollment|grading|assignment|syllabus|cartridge|yui|superclass|brickfield|sitepolicy|confirmation|FormattedMessage))(?:register_new_user|wp_create_user|user_register)\s*\(/gi
|
|
719
726
|
],
|
|
720
727
|
fixSuggestion: 'Add <a href="/privacy">Privacy Policy</a> link to registration form footer',
|
|
721
728
|
penalty: 'Compliance failure',
|
|
@@ -770,9 +777,9 @@ exports.COPPA_RULES = [
|
|
|
770
777
|
/intercom\.init/gi,
|
|
771
778
|
/zendesk\.init/gi,
|
|
772
779
|
/drift\.init/gi,
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
/Freshdesk|FreshChat/gi
|
|
780
|
+
/(?!.*(?:help\.|support\.|docs\.|faq|knowledge.?base|status\.))<script[^>]+src=['"][^'"]*intercom/gi,
|
|
781
|
+
/(?!.*(?:help\.|support\.|docs\.|faq|knowledge.?base|status\.))<script[^>]+src=['"][^'"]*(zendesk|zdassets)/gi,
|
|
782
|
+
/(?!.*(?:help\.|zendesk\.com\/hc|freshdesk\.com\/support|support\.|docs\.|faq|knowledge.?base|status\.))(?:Freshdesk|FreshChat)/gi
|
|
776
783
|
],
|
|
777
784
|
fixSuggestion: 'Disable chat widget for unauthenticated or under-13 users via conditional rendering',
|
|
778
785
|
penalty: '$53,088 per violation',
|
|
@@ -820,25 +827,25 @@ exports.COPPA_RULES = [
|
|
|
820
827
|
severity: 'low',
|
|
821
828
|
description: 'FTC declined to codify push notification restrictions in the 2025 final rule but stated it remains concerned about push notifications and engagement techniques. Best practice: gate push subscriptions behind parental consent. Maps to NGL Labs and Sendit enforcement patterns.',
|
|
822
829
|
patterns: [
|
|
823
|
-
/FirebaseMessaging\.subscribeToTopic/g,
|
|
824
|
-
/OneSignal\.(?:promptForPushNotifications|init)\s*\(/g,
|
|
825
|
-
/sendPushNotification\s*\(/g,
|
|
826
|
-
/fcm\.send\s*\(/g,
|
|
827
|
-
/PushManager\.subscribe\s*\(/g,
|
|
828
|
-
/pushManager\.subscribe\s*\(/g,
|
|
829
|
-
/messaging\(\)\.getToken\s*\(/g,
|
|
830
|
-
/registerForPushNotifications\s*\(/g,
|
|
831
|
-
/addEventListener\s*\(\s*['"]push['"]/g,
|
|
832
|
-
/expo-notifications/g,
|
|
833
|
-
/react-native-push-notification/g,
|
|
830
|
+
/FirebaseMessaging\.subscribeToTopic(?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
831
|
+
/OneSignal\.(?:promptForPushNotifications|init)\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
832
|
+
/sendPushNotification\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
833
|
+
/fcm\.send\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
834
|
+
/PushManager\.subscribe\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
835
|
+
/pushManager\.subscribe\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
836
|
+
/messaging\(\)\.getToken\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
837
|
+
/registerForPushNotifications\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
838
|
+
/addEventListener\s*\(\s*['"]push['"](?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
839
|
+
/expo-notifications(?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
840
|
+
/react-native-push-notification(?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/g,
|
|
834
841
|
// Python — Django push notification libraries
|
|
835
|
-
/(?:import|from)\s+(?:webpush|pywebpush|push_notifications|django_push_notifications)/gi,
|
|
836
|
-
/webpush\.send\s*\(/gi,
|
|
842
|
+
/(?:import|from)\s+(?:webpush|pywebpush|push_notifications|django_push_notifications)(?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/gi,
|
|
843
|
+
/webpush\.send\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/gi,
|
|
837
844
|
// PHP — web-push-php library
|
|
838
|
-
/(?:new\s+)?WebPush\s*\(\s*\[/gi,
|
|
839
|
-
/\$webPush->sendOneNotification/gi,
|
|
845
|
+
/(?:new\s+)?WebPush\s*\(\s*\[(?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/gi,
|
|
846
|
+
/\$webPush->sendOneNotification(?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/gi,
|
|
840
847
|
// Ruby — web-push gem
|
|
841
|
-
/WebPush\.payload_send\s*\(/gi
|
|
848
|
+
/WebPush\.payload_send\s*\((?!.*(?:vendor|node_modules|\.config|\.json|manifest|package\.json|bower|composer|Gemfile|requirements\.txt|Cargo\.toml|\.lock))/gi
|
|
842
849
|
],
|
|
843
850
|
fixSuggestion: 'Gate push notification subscription behind parental dashboard setting',
|
|
844
851
|
penalty: '$53,088 per violation',
|
|
@@ -871,11 +878,11 @@ exports.COPPA_RULES = [
|
|
|
871
878
|
severity: 'medium',
|
|
872
879
|
description: 'DangerouslySetInnerHTML or innerHTML with user-controlled content creates XSS vulnerabilities',
|
|
873
880
|
patterns: [
|
|
874
|
-
/dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*(?!['"]<)[^}]*\}\s*\}/gi,
|
|
881
|
+
/dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*(?!['"]<)(?!.*(?:sanitize|Sanitize|purify|Purify|DOMPurify|escape|Escape|clean|xss|JSON\.stringify|sanitized\w*|purified\w*|escaped\w*|cleaned\w*|safeHtml))[^}]*\}\s*\}/gi,
|
|
875
882
|
/\.innerHTML\s*=\s*\$\{/gi,
|
|
876
|
-
/\.innerHTML\s*=\s*(?!['"]?\s*['"]?\s*;)(?!.*[Ll]ocal(?:ize|ization)
|
|
877
|
-
/\.html\s*\(\s*(?:
|
|
878
|
-
/v-html\s*=\s*["']?(?!.*sanitize)/gi,
|
|
883
|
+
/\.innerHTML\s*=\s*(?!['"]?\s*['"]?\s*;)(?!['"]\s*$)(?!\s*['"]\s*$)(?!.*(?:[Ll]ocal(?:ize|ization)|styleContent|sanitize|Sanitize|purify|Purify|Escape\.html|clean|xss|sanitized\w*|purified\w*|escaped\w*|cleaned\w*|safeHtml))[^;]*\b(?:user[A-Z]\w*|userInput|user_input|formData|query|param|req\.|request\.|body\.|data\.(?:value|content|body|html|text|detail|innerHTML))\w*/gi,
|
|
884
|
+
/\.html\s*\(\s*(?:userInput|user_input|req\.|request\.|params?\.)/gi,
|
|
885
|
+
/v-html\s*=\s*["']?(?!.*(?:sanitize|purify|clean|escape|xss))(?:.*(?:userInput|user_input|user\.input|formData|req\.|request\.|query\.|rawHtml))/gi,
|
|
879
886
|
// PHP — echo/print user input without escaping
|
|
880
887
|
/echo\s+\$_(?:GET|POST|REQUEST)\s*\[/gi,
|
|
881
888
|
// PHP — WordPress unescaped output
|
|
@@ -932,8 +939,8 @@ exports.COPPA_RULES = [
|
|
|
932
939
|
severity: 'medium',
|
|
933
940
|
description: 'External links in child-facing views should trigger a "You are leaving..." modal',
|
|
934
941
|
patterns: [
|
|
935
|
-
|
|
936
|
-
/window\.open\s*\(\s*['"]https?:\/\/(?!.*(?:privacy|terms|legal|tos|policy))/gi
|
|
942
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|i18n|locale|translation|locales?\/|lang\/|messages\.|translations\.|bundle))<a[^>]+href=["']https?:\/\/(?!.*(?:privacy|terms|legal|tos|policy|consent|support|help|docs|documentation))[^"']+["'][^>]*target=["']_blank["'][^>]*>/gi,
|
|
943
|
+
/(?!.*(?:admin|Admin|ADMIN|educator|Educator|instructor|teacher|i18n|locale|translation|locales?\/|lang\/|messages\.|translations\.|bundle))window\.open\s*\(\s*['"]https?:\/\/(?!.*(?:privacy|terms|legal|tos|policy))/gi
|
|
937
944
|
],
|
|
938
945
|
fixSuggestion: 'Wrap external links in SafeLink component with warning modal',
|
|
939
946
|
penalty: 'Warning',
|
|
@@ -1169,7 +1176,7 @@ exports.AI_AUDIT_RULES = [
|
|
|
1169
1176
|
/(?:secret|SECRET|token|TOKEN)\s*[:=]\s*['"](?!process\.env)[a-zA-Z0-9_-]{20,}['"]/gi,
|
|
1170
1177
|
/SUPABASE_(?:ANON_KEY|SERVICE_ROLE_KEY)\s*[:=]\s*['"]ey[a-zA-Z0-9_.+-]{30,}['"]/gi,
|
|
1171
1178
|
/FIREBASE_(?:API_KEY|CONFIG)\s*[:=]\s*['"]AI[a-zA-Z0-9_-]{30,}['"]/gi,
|
|
1172
|
-
/(?:password|passwd|pwd)\s*[:=]\s*['"](?!process\.env)[^'"]{8,}['"]\s*(?:,|;|\})/gi
|
|
1179
|
+
/(?!.*(?:label|Label|LABEL|placeholder|title|message|error|i18n|translation|locale|t\(|__\())(?:password|passwd|pwd)\s*[:=]\s*['"](?!process\.env)[^'"]{8,}['"]\s*(?:,|;|\})/gi
|
|
1173
1180
|
],
|
|
1174
1181
|
fixSuggestion: 'Move all secrets to environment variables. Use process.env.API_KEY or a secrets manager. Never hardcode credentials.',
|
|
1175
1182
|
penalty: 'Security exposure — credentials in source code',
|
|
@@ -1277,11 +1284,11 @@ exports.AU_SBD_RULES = [
|
|
|
1277
1284
|
severity: 'medium',
|
|
1278
1285
|
description: 'Social interaction features (comments, posts, messaging) detected without corresponding report or block mechanisms. AU SbD Principle 2 requires users to have tools to protect themselves from harmful interactions.',
|
|
1279
1286
|
patterns: [
|
|
1280
|
-
/(?:addComment|postComment|submitComment|createComment|commentCreate)\s*(?:=|:|\()/gi,
|
|
1281
|
-
/(?:sendMessage|createMessage|postMessage|submitMessage|messageCreate)\s*(?:=|:|\()/gi,
|
|
1282
|
-
/(?:createPost|submitPost|publishPost|addPost|postCreate)\s*(?:=|:|\()/gi,
|
|
1283
|
-
/(?:addReview|submitReview|createReview|postReview|reviewCreate)\s*(?:=|:|\()/gi,
|
|
1284
|
-
/(?:shareContent|createShare|submitShare)\s*(?:=|:|\()/gi,
|
|
1287
|
+
/(?!.*(?:interface |type |implements |extends |PropTypes|\.d\.ts|@types\/|declare |abstract |readonly |generic |<T>|<T,))(?!.*(?:\?: |: string|: number|: boolean|: any|: void|: never|: unknown|Promise<|Array<|Record<|Partial<|Required<))(?:addComment|postComment|submitComment|createComment|commentCreate)\s*(?:=|:|\()/gi,
|
|
1288
|
+
/(?!.*(?:interface |type |implements |extends |PropTypes|\.d\.ts|@types\/|declare |abstract |readonly |generic |<T>|<T,))(?!.*(?:\?: |: string|: number|: boolean|: any|: void|: never|: unknown|Promise<|Array<|Record<|Partial<|Required<))(?:sendMessage|createMessage|postMessage|submitMessage|messageCreate)\s*(?:=|:|\()/gi,
|
|
1289
|
+
/(?!.*(?:interface |type |implements |extends |PropTypes|\.d\.ts|@types\/|declare |abstract |readonly |generic |<T>|<T,))(?!.*(?:\?: |: string|: number|: boolean|: any|: void|: never|: unknown|Promise<|Array<|Record<|Partial<|Required<))(?:createPost|submitPost|publishPost|addPost|postCreate)\s*(?:=|:|\()/gi,
|
|
1290
|
+
/(?!.*(?:interface |type |implements |extends |PropTypes|\.d\.ts|@types\/|declare |abstract |readonly |generic |<T>|<T,))(?!.*(?:\?: |: string|: number|: boolean|: any|: void|: never|: unknown|Promise<|Array<|Record<|Partial<|Required<))(?:addReview|submitReview|createReview|postReview|reviewCreate)\s*(?:=|:|\()/gi,
|
|
1291
|
+
/(?!.*(?:interface |type |implements |extends |PropTypes|\.d\.ts|@types\/|declare |abstract |readonly |generic |<T>|<T,))(?!.*(?:\?: |: string|: number|: boolean|: any|: void|: never|: unknown|Promise<|Array<|Record<|Partial<|Required<))(?:shareContent|createShare|submitShare)\s*(?:=|:|\()/gi,
|
|
1285
1292
|
],
|
|
1286
1293
|
fixSuggestion: 'Implement report and block mechanisms alongside every social feature. Users must be able to report harmful content and block abusive accounts. AU SbD Principle 2: user empowerment and autonomy.',
|
|
1287
1294
|
penalty: 'Social features without safety controls',
|
|
@@ -1500,7 +1507,7 @@ class HaloEngine {
|
|
|
1500
1507
|
// 1. config.loadedRules — pre-compiled rules from CLI API fetch
|
|
1501
1508
|
// 2. config.rulesPath — YAML file (legacy)
|
|
1502
1509
|
// 3. config.packs — load from bundled rules.json filtered by pack
|
|
1503
|
-
// 4. Default —
|
|
1510
|
+
// 4. Default — Halo 2.0: load ALL packs from bundled rules.json
|
|
1504
1511
|
if (config.loadedRules && config.loadedRules.length > 0) {
|
|
1505
1512
|
// Priority 1: Pre-loaded rules (from CLI API fetch or external source)
|
|
1506
1513
|
this.rules = config.loadedRules;
|
|
@@ -1516,20 +1523,11 @@ class HaloEngine {
|
|
|
1516
1523
|
this.rules = jsonRules.length > 0 ? jsonRules : exports.COPPA_RULES;
|
|
1517
1524
|
}
|
|
1518
1525
|
else {
|
|
1519
|
-
// Priority 4:
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
if (config.ethical) {
|
|
1525
|
-
this.rules = [...this.rules, ...exports.ETHICAL_RULES];
|
|
1526
|
-
}
|
|
1527
|
-
if (config.aiAudit) {
|
|
1528
|
-
this.rules = [...this.rules, ...exports.AI_AUDIT_RULES];
|
|
1529
|
-
}
|
|
1530
|
-
if (config.sectorAuSbd) {
|
|
1531
|
-
this.rules = [...this.rules, ...exports.AU_SBD_RULES];
|
|
1532
|
-
}
|
|
1526
|
+
// Priority 4 (Halo 2.0): Load ALL packs from bundled rules.json
|
|
1527
|
+
// All 160 rules active as loose pre-filters. AI Review Board handles precision.
|
|
1528
|
+
const resolvedPacks = HaloEngine.resolvePacks(config);
|
|
1529
|
+
const jsonRules = this.loadBundledRulesByPack(resolvedPacks);
|
|
1530
|
+
this.rules = jsonRules.length > 0 ? jsonRules : exports.COPPA_RULES;
|
|
1533
1531
|
}
|
|
1534
1532
|
// Sprint 15: Filter out disabled rules.
|
|
1535
1533
|
// Static list ensures rules are disabled regardless of source (API, cache, bundled JSON, hardcoded).
|
|
@@ -1537,29 +1535,22 @@ class HaloEngine {
|
|
|
1537
1535
|
// Sprint 15: Zero-GT rule actions
|
|
1538
1536
|
// DISABLE: zero GT entries, cannot validate precision
|
|
1539
1537
|
// CUT (to proposed tier): zero GT entries, pattern too broad for production
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
'AI-AUDIT-006', // TODO/FIXME compliance — noise in real codebases
|
|
1557
|
-
'AU-SBD-004', // Algorithmic feeds — pattern too broad for production
|
|
1558
|
-
// Sprint 17 Day 0: Zero-GT rules validated against 5 repos — patterns don't match real-world code
|
|
1559
|
-
'ut-sb142-003', // Default DM access — patterns use naming conventions no real app uses (0 hits across Moodle, Discourse, Rocket.Chat, Element, Mastodon)
|
|
1560
|
-
'ut-sb142-004', // Missing parental tools — 2 hits across 5 repos, both FP. API/bundled pattern mismatch. Needs rebuild
|
|
1561
|
-
]);
|
|
1562
|
-
this.rules = this.rules.filter(r => r.is_active !== false && !DISABLED_RULE_IDS.has(r.id));
|
|
1538
|
+
// ── Halo 2.0 (Sprint 18 W2): ALL rules active as loose pre-filters ──
|
|
1539
|
+
// Precision is handled by Tier 3 (AI Review Board), not by disabling rules.
|
|
1540
|
+
// Previously-disabled rules are now active. The AI layer handles false positive
|
|
1541
|
+
// elimination via two-agent consensus, graduated patterns, and enforcement context.
|
|
1542
|
+
// See: Halo 2.0 Architecture doc for rationale.
|
|
1543
|
+
//
|
|
1544
|
+
// Rules with known regex limitations (Sprint 18 W1 analysis):
|
|
1545
|
+
// High-FP (AI handles): coppa-sec-015, coppa-bio-012, AU-SBD-002, coppa-sec-006,
|
|
1546
|
+
// ut-sb142-001, coppa-ext-017, coppa-sec-010, coppa-ugc-014
|
|
1547
|
+
// Low-GT (need more scanning): behavioral-*, aadc-*, lgpd-*, pipeda-*
|
|
1548
|
+
// Zero-GT (untested): ETHICAL-004/005, AU-SBD-003/006, AI-AUDIT-001/003/004/006
|
|
1549
|
+
this.rules = this.rules.filter(r => r.is_active !== false);
|
|
1550
|
+
// Filter by specific rule IDs if provided (e.g., config.rules = ['coppa-auth-001'])
|
|
1551
|
+
if (config.rules && config.rules.length > 0) {
|
|
1552
|
+
this.rules = this.rules.filter(r => config.rules.includes(r.id));
|
|
1553
|
+
}
|
|
1563
1554
|
if (config.severityFilter) {
|
|
1564
1555
|
this.rules = this.rules.filter(r => config.severityFilter.includes(r.severity));
|
|
1565
1556
|
}
|
|
@@ -1578,23 +1569,28 @@ class HaloEngine {
|
|
|
1578
1569
|
}
|
|
1579
1570
|
}
|
|
1580
1571
|
/**
|
|
1581
|
-
* Resolve
|
|
1582
|
-
*
|
|
1583
|
-
*
|
|
1572
|
+
* Resolve pack IDs from config.
|
|
1573
|
+
* Halo 2.0: defaults to ALL packs (loose pre-filter for AI Review Board).
|
|
1574
|
+
* Legacy boolean flags still supported for backward compatibility.
|
|
1584
1575
|
*/
|
|
1585
1576
|
static resolvePacks(config) {
|
|
1586
1577
|
if (config.packs)
|
|
1587
1578
|
return config.packs;
|
|
1588
|
-
|
|
1589
|
-
if (config.ethical)
|
|
1590
|
-
packs
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1579
|
+
// Legacy boolean flags — if ANY are set, use selective mode
|
|
1580
|
+
if (config.ethical || config.aiAudit || config.sectorAuSbd || config.sectorAuOsa) {
|
|
1581
|
+
const packs = ['coppa'];
|
|
1582
|
+
if (config.ethical)
|
|
1583
|
+
packs.push('ethical');
|
|
1584
|
+
if (config.aiAudit)
|
|
1585
|
+
packs.push('ai-audit');
|
|
1586
|
+
if (config.sectorAuSbd)
|
|
1587
|
+
packs.push('au-sbd');
|
|
1588
|
+
if (config.sectorAuOsa)
|
|
1589
|
+
packs.push('au-osa');
|
|
1590
|
+
return packs;
|
|
1591
|
+
}
|
|
1592
|
+
// Halo 2.0 default: all packs active
|
|
1593
|
+
return [...exports.ALL_PACK_IDS];
|
|
1598
1594
|
}
|
|
1599
1595
|
/**
|
|
1600
1596
|
* Get the tree-sitter parser for advanced AST analysis
|
|
@@ -1742,6 +1738,11 @@ class HaloEngine {
|
|
|
1742
1738
|
'coppa-auth-001', // Auth patterns in test helpers
|
|
1743
1739
|
'coppa-sec-015', // XSS patterns in security test cases
|
|
1744
1740
|
'coppa-sec-006', // Sprint 11a: HTTP URLs in test config (e.g., http://example-storage.com in envs/test.py)
|
|
1741
|
+
// Sprint 18: Batch C — bundled JSON rules that FP in test files
|
|
1742
|
+
'dsa-ephemeral-009', // Ephemeral content — test files trigger retention patterns
|
|
1743
|
+
'AU-OSA-009', // AU Online Safety — test mocks trigger safety patterns
|
|
1744
|
+
'AU-OSA-010', // AU Online Safety — test fixtures trigger safety patterns
|
|
1745
|
+
'dpdp-tracking-ban-001', // India DPDP — test configs trigger tracking patterns
|
|
1745
1746
|
]);
|
|
1746
1747
|
// Rules that should be suppressed in consent/compliance implementation files
|
|
1747
1748
|
// These rules flag patterns that are REQUIRED in consent implementations
|
|
@@ -1919,15 +1920,23 @@ class HaloEngine {
|
|
|
1919
1920
|
// Sprint 10: For coppa-sec-015 (XSS): skip innerHTML assignments that are already sanitized
|
|
1920
1921
|
// Y.Escape.html(), DOMPurify.sanitize(), etc. show the developer IS handling XSS
|
|
1921
1922
|
if (rule.id === 'coppa-sec-015') {
|
|
1922
|
-
if (/(?:escape\.html|dompurify|sanitize|purify)\s*\(
|
|
1923
|
+
if (/(?:escape\.html|dompurify|sanitize|purify|xss|\.clean\()\s*\(?/i.test(lineContent)) {
|
|
1924
|
+
continue;
|
|
1925
|
+
}
|
|
1926
|
+
// Also skip if the value is a sanitized variable
|
|
1927
|
+
if (/(?:sanitized|purified|escaped|cleaned|safeHtml)\w*/i.test(lineContent)) {
|
|
1923
1928
|
continue;
|
|
1924
1929
|
}
|
|
1925
1930
|
}
|
|
1926
|
-
// Sprint 10: For coppa-ui-008: skip admin
|
|
1927
|
-
// These are admin/developer-facing forms, not child-facing registration
|
|
1931
|
+
// Sprint 10/18: For coppa-ui-008: skip admin/vendor/widget registration contexts
|
|
1932
|
+
// These are admin/developer-facing forms or vendor widget methods, not child-facing registration
|
|
1928
1933
|
if (rule.id === 'coppa-ui-008') {
|
|
1929
|
-
if (/cartridge[_-]?registration|brickfield|registersetting|tool_configure/i.test(lineContent) ||
|
|
1930
|
-
/cartridge[_-]?registration|brickfield|registersetting/i.test(normalizedPath)) {
|
|
1934
|
+
if (/cartridge[_-]?registration|brickfield|registersetting|tool_configure|sitepolicy|registration_confirmation/i.test(lineContent) ||
|
|
1935
|
+
/cartridge[_-]?registration|brickfield|registersetting|yui2?[-_]container|sitepolicy|registration_confirmation/i.test(normalizedPath)) {
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
// YUI widget method calls (registerForm is a container widget method, not child registration)
|
|
1939
|
+
if (/\.(registerForm|subscribe)\s*\(/i.test(lineContent) && /superclass|subscribe|\.call\b/i.test(lineContent)) {
|
|
1931
1940
|
continue;
|
|
1932
1941
|
}
|
|
1933
1942
|
}
|
|
@@ -2114,5 +2123,28 @@ var framework_detect_1 = require("./framework-detect");
|
|
|
2114
2123
|
Object.defineProperty(exports, "detectFramework", { enumerable: true, get: function () { return framework_detect_1.detectFramework; } });
|
|
2115
2124
|
var index_1 = require("./scaffolds/index");
|
|
2116
2125
|
Object.defineProperty(exports, "SCAFFOLD_REGISTRY", { enumerable: true, get: function () { return index_1.SCAFFOLD_REGISTRY; } });
|
|
2126
|
+
// SDK Intelligence (Sprint 17)
|
|
2127
|
+
var sdk_intelligence_1 = require("./sdk-intelligence");
|
|
2128
|
+
Object.defineProperty(exports, "detectSDKsFromPackageJson", { enumerable: true, get: function () { return sdk_intelligence_1.detectSDKsFromPackageJson; } });
|
|
2129
|
+
Object.defineProperty(exports, "generateSDKContext", { enumerable: true, get: function () { return sdk_intelligence_1.generateSDKContext; } });
|
|
2130
|
+
Object.defineProperty(exports, "detectSDKs", { enumerable: true, get: function () { return sdk_intelligence_1.detectSDKs; } });
|
|
2131
|
+
Object.defineProperty(exports, "SDK_RISK_DATABASE", { enumerable: true, get: function () { return sdk_intelligence_1.SDK_RISK_DATABASE; } });
|
|
2132
|
+
// Tier Context (Sprint 19) — 4-tier feature gating
|
|
2133
|
+
var tier_context_1 = require("./tier-context");
|
|
2134
|
+
Object.defineProperty(exports, "createTierContext", { enumerable: true, get: function () { return tier_context_1.createTierContext; } });
|
|
2135
|
+
Object.defineProperty(exports, "resolveTierFromKey", { enumerable: true, get: function () { return tier_context_1.resolveTierFromKey; } });
|
|
2136
|
+
Object.defineProperty(exports, "getUpgradeCTA", { enumerable: true, get: function () { return tier_context_1.getUpgradeCTA; } });
|
|
2137
|
+
Object.defineProperty(exports, "getTierComparison", { enumerable: true, get: function () { return tier_context_1.getTierComparison; } });
|
|
2138
|
+
Object.defineProperty(exports, "TIER_LIMITS", { enumerable: true, get: function () { return tier_context_1.TIER_LIMITS; } });
|
|
2139
|
+
Object.defineProperty(exports, "TIER_FEATURES", { enumerable: true, get: function () { return tier_context_1.TIER_FEATURES; } });
|
|
2140
|
+
Object.defineProperty(exports, "TIER_DISPLAY_NAMES", { enumerable: true, get: function () { return tier_context_1.TIER_DISPLAY_NAMES; } });
|
|
2141
|
+
// COPPA 2.0 Countdown (Sprint 19) — shared across all output surfaces
|
|
2142
|
+
var coppa_countdown_1 = require("./coppa-countdown");
|
|
2143
|
+
Object.defineProperty(exports, "getCoppaCountdown", { enumerable: true, get: function () { return coppa_countdown_1.getCoppaCountdown; } });
|
|
2144
|
+
Object.defineProperty(exports, "formatCoppaCountdownCLI", { enumerable: true, get: function () { return coppa_countdown_1.formatCoppaCountdownCLI; } });
|
|
2145
|
+
Object.defineProperty(exports, "formatCoppaCountdownMarkdown", { enumerable: true, get: function () { return coppa_countdown_1.formatCoppaCountdownMarkdown; } });
|
|
2146
|
+
Object.defineProperty(exports, "formatCoppaCountdownPDF", { enumerable: true, get: function () { return coppa_countdown_1.formatCoppaCountdownPDF; } });
|
|
2147
|
+
Object.defineProperty(exports, "COPPA_2_ENFORCEMENT_DATE", { enumerable: true, get: function () { return coppa_countdown_1.COPPA_2_ENFORCEMENT_DATE; } });
|
|
2148
|
+
Object.defineProperty(exports, "PENALTY_PER_VIOLATION_PER_DAY", { enumerable: true, get: function () { return coppa_countdown_1.PENALTY_PER_VIOLATION_PER_DAY; } });
|
|
2117
2149
|
exports.default = HaloEngine;
|
|
2118
2150
|
//# sourceMappingURL=index.js.map
|