@runhalo/engine 0.2.0 → 0.3.1
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/framework-detect.d.ts +15 -0
- package/dist/framework-detect.js +108 -0
- package/dist/framework-detect.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +326 -14
- package/dist/index.js.map +1 -1
- package/dist/scaffold-engine.d.ts +67 -0
- package/dist/scaffold-engine.js +104 -0
- package/dist/scaffold-engine.js.map +1 -0
- package/dist/scaffolds/index.d.ts +23 -0
- package/dist/scaffolds/index.js +19 -0
- package/dist/scaffolds/index.js.map +1 -0
- package/dist/scaffolds/templates/age-gate-auth.d.ts +7 -0
- package/dist/scaffolds/templates/age-gate-auth.js +254 -0
- package/dist/scaffolds/templates/age-gate-auth.js.map +1 -0
- package/dist/scaffolds/templates/consent-cookies.d.ts +7 -0
- package/dist/scaffolds/templates/consent-cookies.js +253 -0
- package/dist/scaffolds/templates/consent-cookies.js.map +1 -0
- package/dist/scaffolds/templates/pii-sanitizer.d.ts +7 -0
- package/dist/scaffolds/templates/pii-sanitizer.js +263 -0
- package/dist/scaffolds/templates/pii-sanitizer.js.map +1 -0
- package/dist/scaffolds/templates/retention-policy.d.ts +7 -0
- package/dist/scaffolds/templates/retention-policy.js +247 -0
- package/dist/scaffolds/templates/retention-policy.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Detection — detects project framework from package.json
|
|
3
|
+
* Used by ScaffoldEngine to generate framework-specific code scaffolds
|
|
4
|
+
*/
|
|
5
|
+
export type Framework = 'react' | 'nextjs' | 'vue' | 'svelte' | 'plain-js';
|
|
6
|
+
export interface FrameworkDetectionResult {
|
|
7
|
+
framework: Framework;
|
|
8
|
+
typescript: boolean;
|
|
9
|
+
confidence: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Detect the project framework by reading package.json
|
|
13
|
+
* Priority: next > react > vue > svelte > plain-js
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectFramework(projectPath: string): FrameworkDetectionResult;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Framework Detection — detects project framework from package.json
|
|
4
|
+
* Used by ScaffoldEngine to generate framework-specific code scaffolds
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.detectFramework = detectFramework;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* Detect the project framework by reading package.json
|
|
45
|
+
* Priority: next > react > vue > svelte > plain-js
|
|
46
|
+
*/
|
|
47
|
+
function detectFramework(projectPath) {
|
|
48
|
+
const result = {
|
|
49
|
+
framework: 'plain-js',
|
|
50
|
+
typescript: false,
|
|
51
|
+
confidence: 0.3,
|
|
52
|
+
};
|
|
53
|
+
// Try to read package.json
|
|
54
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
55
|
+
let pkg = null;
|
|
56
|
+
try {
|
|
57
|
+
if (fs.existsSync(pkgPath)) {
|
|
58
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Malformed package.json — fall through to defaults
|
|
63
|
+
}
|
|
64
|
+
if (!pkg) {
|
|
65
|
+
// Check for tsconfig.json even without package.json
|
|
66
|
+
const tsconfigPath = path.join(projectPath, 'tsconfig.json');
|
|
67
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
68
|
+
result.typescript = true;
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
const deps = pkg.dependencies || {};
|
|
73
|
+
const devDeps = pkg.devDependencies || {};
|
|
74
|
+
const allDeps = { ...deps, ...devDeps };
|
|
75
|
+
// Detect TypeScript
|
|
76
|
+
if (allDeps['typescript']) {
|
|
77
|
+
result.typescript = true;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const tsconfigPath = path.join(projectPath, 'tsconfig.json');
|
|
81
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
82
|
+
result.typescript = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Detect framework (priority order — Next.js includes React, so check first)
|
|
86
|
+
if (deps['next'] || devDeps['next']) {
|
|
87
|
+
result.framework = 'nextjs';
|
|
88
|
+
result.confidence = 0.95;
|
|
89
|
+
}
|
|
90
|
+
else if (deps['react'] || devDeps['react']) {
|
|
91
|
+
result.framework = 'react';
|
|
92
|
+
result.confidence = 0.9;
|
|
93
|
+
}
|
|
94
|
+
else if (deps['vue'] || devDeps['vue']) {
|
|
95
|
+
result.framework = 'vue';
|
|
96
|
+
result.confidence = 0.9;
|
|
97
|
+
}
|
|
98
|
+
else if (deps['svelte'] || devDeps['svelte']) {
|
|
99
|
+
result.framework = 'svelte';
|
|
100
|
+
result.confidence = 0.9;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
result.framework = 'plain-js';
|
|
104
|
+
result.confidence = 0.5;
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=framework-detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framework-detect.js","sourceRoot":"","sources":["../src/framework-detect.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBH,0CA6DC;AA5ED,uCAAyB;AACzB,2CAA6B;AAU7B;;;GAGG;AACH,SAAgB,eAAe,CAAC,WAAmB;IACjD,MAAM,MAAM,GAA6B;QACvC,SAAS,EAAE,UAAU;QACrB,UAAU,EAAE,KAAK;QACjB,UAAU,EAAE,GAAG;KAChB,CAAC;IAEF,2BAA2B;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IACvD,IAAI,GAAG,GAAQ,IAAI,CAAC;IAEpB,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IAED,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,oDAAoD;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAC7D,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;IAExC,oBAAoB;IACpB,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAC7D,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC5B,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;IAC3B,CAAC;SAAM,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC;QAC3B,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;IAC1B,CAAC;SAAM,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC;QACzB,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;IAC1B,CAAC;SAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC5B,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,SAAS,GAAG,UAAU,CAAC;QAC9B,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;IAC1B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -79,6 +79,8 @@ declare const REMEDIATION_MAP: Record<string, RemediationSpec>;
|
|
|
79
79
|
declare function getRemediation(ruleId: string): RemediationSpec;
|
|
80
80
|
export declare const COPPA_RULES: Rule[];
|
|
81
81
|
export declare const ETHICAL_RULES: Rule[];
|
|
82
|
+
export declare const AI_AUDIT_RULES: Rule[];
|
|
83
|
+
export declare const AU_SBD_RULES: Rule[];
|
|
82
84
|
export interface IgnoreConfig {
|
|
83
85
|
/** File glob patterns to ignore entirely */
|
|
84
86
|
ignoredFiles: string[];
|
|
@@ -120,6 +122,8 @@ export interface EngineConfig {
|
|
|
120
122
|
ignoreConfig?: IgnoreConfig;
|
|
121
123
|
projectDomains?: string[];
|
|
122
124
|
ethical?: boolean;
|
|
125
|
+
aiAudit?: boolean;
|
|
126
|
+
sectorAuSbd?: boolean;
|
|
123
127
|
}
|
|
124
128
|
export interface ScanResult {
|
|
125
129
|
filePath: string;
|
|
@@ -173,4 +177,10 @@ export type { FixResult, FileFixResult, FixOptions } from './fixer';
|
|
|
173
177
|
export { transformUrlUpgrade, transformRemoveDefault, transformSanitizeInput, transformSetDefault, } from './fixer';
|
|
174
178
|
export { ComplianceScoreEngine } from './scoring';
|
|
175
179
|
export type { ComplianceScoreResult, LetterGrade } from './scoring';
|
|
180
|
+
export { ScaffoldEngine } from './scaffold-engine';
|
|
181
|
+
export type { GuidedFixResult, GuidedFixSummary } from './scaffold-engine';
|
|
182
|
+
export { detectFramework } from './framework-detect';
|
|
183
|
+
export type { Framework, FrameworkDetectionResult } from './framework-detect';
|
|
184
|
+
export { SCAFFOLD_REGISTRY } from './scaffolds/index';
|
|
185
|
+
export type { ScaffoldTemplate, ScaffoldFile } from './scaffolds/index';
|
|
176
186
|
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.ComplianceScoreEngine = exports.transformSetDefault = exports.transformSanitizeInput = exports.transformRemoveDefault = exports.transformUrlUpgrade = exports.FixEngine = exports.REMEDIATION_MAP = exports.HaloEngine = exports.ETHICAL_RULES = exports.COPPA_RULES = exports.treeSitterParser = exports.TreeSitterParser = void 0;
|
|
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;
|
|
48
48
|
exports.loadRulesFromYAML = loadRulesFromYAML;
|
|
49
49
|
exports.parseHaloignore = parseHaloignore;
|
|
50
50
|
exports.shouldIgnoreFile = shouldIgnoreFile;
|
|
@@ -56,10 +56,14 @@ const tree_sitter_1 = __importDefault(require("tree-sitter"));
|
|
|
56
56
|
const tree_sitter_typescript_1 = __importDefault(require("tree-sitter-typescript"));
|
|
57
57
|
const tree_sitter_javascript_1 = __importDefault(require("tree-sitter-javascript"));
|
|
58
58
|
const yaml = __importStar(require("js-yaml"));
|
|
59
|
-
// Extract category from ruleId (e.g. "coppa-auth-001" → "auth", "ETHICAL-001" → "ethical")
|
|
59
|
+
// Extract category from ruleId (e.g. "coppa-auth-001" → "auth", "ETHICAL-001" → "ethical", "AU-SBD-001" → "au-sbd")
|
|
60
60
|
function extractCategory(ruleId) {
|
|
61
61
|
if (ruleId.startsWith('ETHICAL'))
|
|
62
62
|
return 'ethical';
|
|
63
|
+
if (ruleId.startsWith('AI-AUDIT'))
|
|
64
|
+
return 'ai-audit';
|
|
65
|
+
if (ruleId.startsWith('AU-SBD'))
|
|
66
|
+
return 'au-sbd';
|
|
63
67
|
const match = ruleId.match(/^coppa-(\w+)-\d+$/);
|
|
64
68
|
return match ? match[1] : 'unknown';
|
|
65
69
|
}
|
|
@@ -71,7 +75,8 @@ function detectLanguage(filePath) {
|
|
|
71
75
|
'.py': 'python', '.swift': 'swift', '.java': 'java', '.kt': 'kotlin',
|
|
72
76
|
'.html': 'html', '.vue': 'vue', '.svelte': 'svelte', '.php': 'php',
|
|
73
77
|
'.cpp': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.cs': 'csharp',
|
|
74
|
-
'.qml': 'qml', '.sql': 'sql',
|
|
78
|
+
'.qml': 'qml', '.sql': 'sql', '.go': 'go', '.rb': 'ruby',
|
|
79
|
+
'.xml': 'xml', '.erb': 'ruby',
|
|
75
80
|
};
|
|
76
81
|
return langMap[ext] || 'unknown';
|
|
77
82
|
}
|
|
@@ -263,15 +268,37 @@ exports.COPPA_RULES = [
|
|
|
263
268
|
severity: 'critical',
|
|
264
269
|
description: 'Social login (Google, Facebook, Twitter) without age gating is prohibited for child-directed apps',
|
|
265
270
|
patterns: [
|
|
271
|
+
// JS/TS — Firebase
|
|
266
272
|
/signInWithPopup\s*\(\s*\w+\s*,\s*['"](google|facebook|twitter|github)['"]/gi,
|
|
267
273
|
/signInWithPopup\s*\(\s*['"](google|facebook|twitter|github)['"]/gi,
|
|
268
274
|
/signInWithPopup\s*\(\s*\w+\s*,\s*\w+\s*\)/gi,
|
|
269
275
|
/firebase\.auth\(\)\s*\.\s*signInWithPopup/gi,
|
|
270
|
-
/
|
|
276
|
+
// JS/TS — Passport.js
|
|
277
|
+
/passport\.authenticate\s*\(\s*['"](google|facebook|twitter)['"]/gi,
|
|
278
|
+
// Python — django-allauth social providers config
|
|
279
|
+
/SOCIALACCOUNT_PROVIDERS\s*=\s*\{[^}]*(?:google|facebook|twitter|github)/gi,
|
|
280
|
+
// Python — python-social-auth backends
|
|
281
|
+
/SOCIAL_AUTH_(?:GOOGLE|FACEBOOK|TWITTER|GITHUB)_(?:KEY|SECRET)/gi,
|
|
282
|
+
// Python — flask-dance blueprints
|
|
283
|
+
/make_(?:google|facebook|twitter|github)_blueprint\s*\(/gi,
|
|
284
|
+
// Python — authlib OAuth registration
|
|
285
|
+
/oauth\.register\s*\(\s*['"](?:google|facebook|twitter|github)['"]/gi,
|
|
286
|
+
// Go — goth social auth providers
|
|
287
|
+
/goth\.UseProviders\s*\(/gi,
|
|
288
|
+
// Java — Spring Security OAuth2 login
|
|
289
|
+
/\.oauth2Login\s*\(\s*\)/gi,
|
|
290
|
+
// Java — Spring OAuth2 client registration
|
|
291
|
+
/ClientRegistration\.withRegistrationId\s*\(\s*['"](?:google|facebook|twitter|github)['"]/gi,
|
|
292
|
+
// Kotlin/Java — Firebase Android
|
|
293
|
+
/Firebase\.auth\.signInWithCredential/gi,
|
|
294
|
+
// Kotlin/Java — Google Sign-In Android
|
|
295
|
+
/GoogleSignIn\.getClient\s*\(/gi,
|
|
296
|
+
// Kotlin/Java — Facebook Login Android SDK
|
|
297
|
+
/LoginManager\.getInstance\s*\(\s*\)\s*\.logIn/gi
|
|
271
298
|
],
|
|
272
299
|
fixSuggestion: 'Wrap the auth call in a conditional check for user.age >= 13 or use signInWithParentEmail() for children',
|
|
273
300
|
penalty: '$51,744 per violation',
|
|
274
|
-
languages: ['typescript', 'javascript', 'python', 'swift']
|
|
301
|
+
languages: ['typescript', 'javascript', 'python', 'go', 'java', 'kotlin', 'swift']
|
|
275
302
|
},
|
|
276
303
|
{
|
|
277
304
|
id: 'coppa-data-002',
|
|
@@ -310,14 +337,30 @@ exports.COPPA_RULES = [
|
|
|
310
337
|
severity: 'high',
|
|
311
338
|
description: 'High-accuracy geolocation without parental consent is prohibited',
|
|
312
339
|
patterns: [
|
|
340
|
+
// JS/TS — browser Geolocation API
|
|
313
341
|
/navigator\.geolocation\.getCurrentPosition/gi,
|
|
314
342
|
/navigator\.geolocation\.watchPosition/gi,
|
|
343
|
+
// Swift — CoreLocation
|
|
315
344
|
/CLLocationManager\.startUpdatingLocation\(\)/gi,
|
|
316
|
-
/locationServices\.requestLocation/gi
|
|
345
|
+
/locationServices\.requestLocation/gi,
|
|
346
|
+
// Java Android — LocationManager
|
|
347
|
+
/LocationManager\s*\.\s*requestLocationUpdates\s*\(/gi,
|
|
348
|
+
// Java/Kotlin Android — Fused Location Provider (Google Play Services)
|
|
349
|
+
/FusedLocationProviderClient|fusedLocationClient\s*\.\s*(?:requestLocationUpdates|getLastLocation|getCurrentLocation)/gi,
|
|
350
|
+
// Java Android — high accuracy priority
|
|
351
|
+
/LocationRequest\.create\s*\(\s*\)\s*\.\s*setPriority\s*\(\s*LocationRequest\.PRIORITY_HIGH_ACCURACY/gi,
|
|
352
|
+
// Kotlin Android — LocationRequest.Builder
|
|
353
|
+
/LocationRequest\.Builder\s*\(\s*Priority\.PRIORITY_HIGH_ACCURACY/gi,
|
|
354
|
+
// Python — geocoder library
|
|
355
|
+
/geocoder\.(?:ip|google|osm|mapquest)\s*\(/gi,
|
|
356
|
+
// Python — geopy geolocators
|
|
357
|
+
/(?:Nominatim|GoogleV3|Bing)\s*\([^)]*\)\s*\.(?:geocode|reverse)/gi,
|
|
358
|
+
// Android manifest — fine location permission
|
|
359
|
+
/android\.permission\.ACCESS_FINE_LOCATION/gi
|
|
317
360
|
],
|
|
318
361
|
fixSuggestion: 'Downgrade accuracy to kCLLocationAccuracyThreeKilometers or require parental consent',
|
|
319
362
|
penalty: '$51,744 per violation',
|
|
320
|
-
languages: ['typescript', 'javascript', 'swift', 'kotlin']
|
|
363
|
+
languages: ['typescript', 'javascript', 'swift', 'kotlin', 'java', 'python', 'xml']
|
|
321
364
|
},
|
|
322
365
|
{
|
|
323
366
|
id: 'coppa-retention-005',
|
|
@@ -325,11 +368,22 @@ exports.COPPA_RULES = [
|
|
|
325
368
|
severity: 'medium',
|
|
326
369
|
description: 'User schemas must have deleted_at, expiration_date, or TTL index for data retention',
|
|
327
370
|
patterns: [
|
|
328
|
-
/
|
|
371
|
+
// JS/TS — Mongoose schemas
|
|
372
|
+
/new\s+Schema\s*\(\s*\{[^{}]*\}/gi,
|
|
373
|
+
// Python — Django models
|
|
374
|
+
/class\s+(?:User|Child|Student|Profile|Account|Member)\w*\s*\(\s*models\.Model\s*\)/gi,
|
|
375
|
+
// Python — SQLAlchemy declarative models
|
|
376
|
+
/class\s+(?:User|Child|Student|Profile|Account|Member)\w*\s*\(\s*(?:Base|db\.Model)\s*\)/gi,
|
|
377
|
+
// Go — GORM model structs with user-related names
|
|
378
|
+
/type\s+(?:User|Child|Student|Profile|Account|Member)\w*\s+struct\s*\{/gi,
|
|
379
|
+
// Java/Kotlin — JPA @Entity on user-related classes
|
|
380
|
+
/@Entity[\s\S]*?class\s+(?:User|Child|Student|Profile|Account|Member)/gi,
|
|
381
|
+
// Kotlin — data class for user models
|
|
382
|
+
/data\s+class\s+(?:User|Child|Student|Profile|Account|Member)\w*\s*\(/gi
|
|
329
383
|
],
|
|
330
384
|
fixSuggestion: 'Add deleted_at column, expiration_date field, or TTL index to database schema',
|
|
331
385
|
penalty: 'Regulatory audit failure',
|
|
332
|
-
languages: ['typescript', 'javascript', 'python', '
|
|
386
|
+
languages: ['typescript', 'javascript', 'python', 'go', 'java', 'kotlin', 'sql']
|
|
333
387
|
},
|
|
334
388
|
// ========== Rules 6-20 (Sprint 2) ==========
|
|
335
389
|
// Rule 6: Unencrypted PII Transmission
|
|
@@ -520,13 +574,24 @@ exports.COPPA_RULES = [
|
|
|
520
574
|
severity: 'low',
|
|
521
575
|
description: 'Cookies or localStorage storing tracking data or PII requires a consent banner',
|
|
522
576
|
patterns: [
|
|
577
|
+
// JS/TS — browser APIs
|
|
523
578
|
/document\.cookie\s*=\s*[^;]*(?:user|email|name|token|session|track|id|uid|analytics)/gi,
|
|
524
579
|
/localStorage\.setItem\s*\(\s*['"][^'"]*(?:user|email|token|session|track|auth|login|id|uid|analytics)[^'"]*['"]/gi,
|
|
525
|
-
/sessionStorage\.setItem\s*\(\s*['"][^'"]*(?:user|email|token|session|track|auth|login|id|uid|analytics)[^'"]*['"]/gi
|
|
580
|
+
/sessionStorage\.setItem\s*\(\s*['"][^'"]*(?:user|email|token|session|track|auth|login|id|uid|analytics)[^'"]*['"]/gi,
|
|
581
|
+
// Python — Flask/Django response.set_cookie()
|
|
582
|
+
/\.set_cookie\s*\(\s*['"][^'"]*(?:user|email|token|session|track|auth|login|uid|analytics)[^'"]*['"]/gi,
|
|
583
|
+
// Go — net/http SetCookie
|
|
584
|
+
/http\.SetCookie\s*\(\s*\w+\s*,\s*&http\.Cookie\s*\{/gi,
|
|
585
|
+
// Java/Kotlin — HttpServletResponse.addCookie
|
|
586
|
+
/\.addCookie\s*\(\s*new\s+Cookie\s*\(/gi,
|
|
587
|
+
// Java/Kotlin — Spring ResponseCookie
|
|
588
|
+
/ResponseCookie\.from\s*\(/gi,
|
|
589
|
+
// Generic — any language setting cookies with PII field names
|
|
590
|
+
/(?:set_cookie|SetCookie|addCookie|add_cookie)\s*\([^)]*(?:user|email|token|session|track|auth|uid|analytics)/gi
|
|
526
591
|
],
|
|
527
592
|
fixSuggestion: 'Add a cookie consent banner component before setting tracking or PII cookies',
|
|
528
593
|
penalty: 'Compliance warning',
|
|
529
|
-
languages: ['typescript', 'javascript']
|
|
594
|
+
languages: ['typescript', 'javascript', 'python', 'go', 'java', 'kotlin']
|
|
530
595
|
},
|
|
531
596
|
// Rule 17: External Links to Non-Child-Safe Sites
|
|
532
597
|
// Fixed Sprint 4: Exclude privacy/TOS links, mailto, and common safe targets
|
|
@@ -550,15 +615,30 @@ exports.COPPA_RULES = [
|
|
|
550
615
|
severity: 'high',
|
|
551
616
|
description: 'Passing email, name, or phone to analytics.identify() exposes PII to third parties',
|
|
552
617
|
patterns: [
|
|
618
|
+
// JS/TS — client-side analytics SDKs
|
|
553
619
|
/analytics\.identify\s*\([^)]*email/gi,
|
|
554
620
|
/mixpanel\.identify.*email/gi,
|
|
555
621
|
/segment\.identify.*email/gi,
|
|
556
622
|
/amplitude\.identify.*email/gi,
|
|
557
|
-
/identify\s*\(\s*\{[^}]*(?:email|name|phone)[^}]*\}/gi
|
|
623
|
+
/identify\s*\(\s*\{[^}]*(?:email|name|phone)[^}]*\}/gi,
|
|
624
|
+
// Python — Segment analytics-python
|
|
625
|
+
/analytics\.identify\s*\(\s*\w+\s*,\s*\{[^}]*(?:email|name|phone)/gi,
|
|
626
|
+
// Python — Mixpanel people_set with PII
|
|
627
|
+
/mp\.people_set\s*\([^)]*(?:email|\$email|name|phone)/gi,
|
|
628
|
+
// Go — Segment analytics-go Identify with PII
|
|
629
|
+
/analytics\.Enqueue\s*\(\s*analytics\.Identify\s*\{[^}]*(?:Email|Name|Phone)/gi,
|
|
630
|
+
// Java/Kotlin — Amplitude setUserId with PII
|
|
631
|
+
/Amplitude\.getInstance\s*\(\s*\)\s*\.setUserId\s*\([^)]*email/gi,
|
|
632
|
+
// Java/Kotlin — Mixpanel identify with email
|
|
633
|
+
/MixpanelAPI\.\w*identify\s*\([^)]*email/gi,
|
|
634
|
+
// Java/Kotlin — Firebase Analytics with PII
|
|
635
|
+
/FirebaseAnalytics\.setUserId\s*\([^)]*(?:email|name)/gi,
|
|
636
|
+
// Generic — setUserId with email across languages
|
|
637
|
+
/(?:setUserId|set_user_id)\s*\([^)]*(?:email|\.name|phone)/gi
|
|
558
638
|
],
|
|
559
639
|
fixSuggestion: 'Hash user ID and omit email/name from analytics payload',
|
|
560
640
|
penalty: '$51,744 per violation',
|
|
561
|
-
languages: ['typescript', 'javascript']
|
|
641
|
+
languages: ['typescript', 'javascript', 'python', 'go', 'java', 'kotlin']
|
|
562
642
|
},
|
|
563
643
|
// Rule 19: School Official Consent Bypass
|
|
564
644
|
// Fixed Sprint 4: Tightened patterns to match actual auth/registration flows only
|
|
@@ -687,6 +767,222 @@ exports.ETHICAL_RULES = [
|
|
|
687
767
|
languages: ['typescript', 'javascript', 'tsx', 'jsx', 'html']
|
|
688
768
|
}
|
|
689
769
|
];
|
|
770
|
+
// AI-Generated Code Audit Rules
|
|
771
|
+
// Catches patterns commonly introduced by AI coding assistants (Copilot, Claude, Cursor, etc.)
|
|
772
|
+
// that create COPPA compliance risks. AI models often reproduce training data patterns
|
|
773
|
+
// including analytics boilerplate, insecure defaults, and placeholder credentials.
|
|
774
|
+
exports.AI_AUDIT_RULES = [
|
|
775
|
+
// AI-AUDIT-001: Placeholder Analytics
|
|
776
|
+
{
|
|
777
|
+
id: 'AI-AUDIT-001',
|
|
778
|
+
name: 'Placeholder Analytics Script',
|
|
779
|
+
severity: 'high',
|
|
780
|
+
description: 'AI-generated code frequently includes placeholder analytics (UA-XXXXX, G-XXXXXX, fbq) copied from training data. These may activate real tracking without child_directed_treatment flags.',
|
|
781
|
+
patterns: [
|
|
782
|
+
/gtag\s*\(\s*['"]config['"]\s*,\s*['"](?:UA-|G-)X{3,}['"]/gi,
|
|
783
|
+
/fbq\s*\(\s*['"]init['"]\s*,\s*['"](?:0{5,}|1{5,}|X{5,}|YOUR_|PIXEL_ID|123456789)['"]/gi,
|
|
784
|
+
/ga\s*\(\s*['"]create['"]\s*,\s*['"]UA-(?:0{5,}|X{5,}|YOUR_)['"]/gi,
|
|
785
|
+
/['"](?:UA|G)-(?:XXXXXXX|0000000|YOUR_ID|REPLACE_ME)['"]/gi,
|
|
786
|
+
/analytics_id\s*[:=]\s*['"](?:placeholder|test|example|TODO|FIXME)['"]/gi
|
|
787
|
+
],
|
|
788
|
+
fixSuggestion: 'Remove placeholder analytics IDs. If analytics are needed, use a COPPA-compliant provider with child_directed_treatment: true.',
|
|
789
|
+
penalty: 'AI-generated compliance risk',
|
|
790
|
+
languages: ['typescript', 'javascript', 'html']
|
|
791
|
+
},
|
|
792
|
+
// AI-AUDIT-002: Hardcoded Secrets
|
|
793
|
+
{
|
|
794
|
+
id: 'AI-AUDIT-002',
|
|
795
|
+
name: 'AI-Generated Hardcoded Secrets',
|
|
796
|
+
severity: 'critical',
|
|
797
|
+
description: 'AI coding assistants frequently generate placeholder API keys, tokens, and secrets inline. These may be committed to version control and exposed.',
|
|
798
|
+
patterns: [
|
|
799
|
+
/(?:api_?key|apiKey|API_KEY)\s*[:=]\s*['"](?:sk-|pk-|ak-|key-)[a-zA-Z0-9]{10,}['"]/gi,
|
|
800
|
+
/(?:secret|SECRET|token|TOKEN)\s*[:=]\s*['"](?!process\.env)[a-zA-Z0-9_-]{20,}['"]/gi,
|
|
801
|
+
/SUPABASE_(?:ANON_KEY|SERVICE_ROLE_KEY)\s*[:=]\s*['"]ey[a-zA-Z0-9_.+-]{30,}['"]/gi,
|
|
802
|
+
/FIREBASE_(?:API_KEY|CONFIG)\s*[:=]\s*['"]AI[a-zA-Z0-9_-]{30,}['"]/gi,
|
|
803
|
+
/(?:password|passwd|pwd)\s*[:=]\s*['"](?!process\.env)[^'"]{8,}['"]\s*(?:,|;|\})/gi
|
|
804
|
+
],
|
|
805
|
+
fixSuggestion: 'Move all secrets to environment variables. Use process.env.API_KEY or a secrets manager. Never hardcode credentials.',
|
|
806
|
+
penalty: 'Security exposure — credentials in source code',
|
|
807
|
+
languages: ['typescript', 'javascript', 'python', 'java']
|
|
808
|
+
},
|
|
809
|
+
// AI-AUDIT-003: Hallucinated URLs
|
|
810
|
+
{
|
|
811
|
+
id: 'AI-AUDIT-003',
|
|
812
|
+
name: 'Hallucinated/Placeholder API URLs',
|
|
813
|
+
severity: 'medium',
|
|
814
|
+
description: 'AI models often generate fake API endpoints (api.example.com, jsonplaceholder, reqres.in) that may be replaced with real endpoints without proper review.',
|
|
815
|
+
patterns: [
|
|
816
|
+
/fetch\s*\(\s*['"]https?:\/\/(?:api\.example\.com|jsonplaceholder\.typicode\.com|reqres\.in|httpbin\.org|mockapi\.io|dummyjson\.com)[^'"]*['"]/gi,
|
|
817
|
+
/axios\.\w+\s*\(\s*['"]https?:\/\/(?:api\.example\.com|jsonplaceholder\.typicode\.com|reqres\.in|httpbin\.org|dummyjson\.com)[^'"]*['"]/gi,
|
|
818
|
+
/(?:BASE_URL|API_URL|ENDPOINT)\s*[:=]\s*['"]https?:\/\/(?:api\.example\.com|your-api|my-api|TODO|REPLACE)[^'"]*['"]/gi
|
|
819
|
+
],
|
|
820
|
+
fixSuggestion: 'Replace placeholder URLs with actual endpoints from environment variables. Review all API calls for COPPA data handling compliance.',
|
|
821
|
+
penalty: 'AI-generated placeholder risk',
|
|
822
|
+
languages: ['typescript', 'javascript', 'python']
|
|
823
|
+
},
|
|
824
|
+
// AI-AUDIT-004: Copy-Paste Tracking Boilerplate
|
|
825
|
+
{
|
|
826
|
+
id: 'AI-AUDIT-004',
|
|
827
|
+
name: 'Copy-Paste Tracking Boilerplate',
|
|
828
|
+
severity: 'high',
|
|
829
|
+
description: 'AI assistants reproduce common analytics setup patterns from training data. These often include user identification, event tracking, and session recording without consent flows.',
|
|
830
|
+
patterns: [
|
|
831
|
+
/hotjar\.init\s*\(/gi,
|
|
832
|
+
/Sentry\.init\s*\(\s*\{[^}]*dsn/gi,
|
|
833
|
+
/LogRocket\.init\s*\(/gi,
|
|
834
|
+
/FullStory\.init\s*\(/gi,
|
|
835
|
+
/heap\.load\s*\(/gi,
|
|
836
|
+
/posthog\.init\s*\(/gi,
|
|
837
|
+
/amplitude\.init\s*\(/gi,
|
|
838
|
+
/mixpanel\.init\s*\(/gi,
|
|
839
|
+
/window\.clarity\s*\(/gi
|
|
840
|
+
],
|
|
841
|
+
fixSuggestion: 'Remove session recording and analytics initialization unless COPPA consent is obtained. These tools capture keystrokes, mouse movements, and user behavior — all PII for children.',
|
|
842
|
+
penalty: 'Third-party data collection without consent',
|
|
843
|
+
languages: ['typescript', 'javascript', 'html']
|
|
844
|
+
},
|
|
845
|
+
// AI-AUDIT-005: Insecure Defaults
|
|
846
|
+
{
|
|
847
|
+
id: 'AI-AUDIT-005',
|
|
848
|
+
name: 'AI-Generated Insecure Defaults',
|
|
849
|
+
severity: 'medium',
|
|
850
|
+
description: 'AI models commonly generate code with insecure default configurations: CORS *, disabled SSL verification, permissive CSP, or open CORS origins.',
|
|
851
|
+
patterns: [
|
|
852
|
+
/cors\s*\(\s*\{\s*origin\s*:\s*(?:['"]?\*['"]?|true)/gi,
|
|
853
|
+
/Access-Control-Allow-Origin['"]\s*,\s*['"]\*/gi,
|
|
854
|
+
/rejectUnauthorized\s*:\s*false/gi,
|
|
855
|
+
/NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*['"]?0['"]?/gi,
|
|
856
|
+
/content-security-policy['"]\s*,\s*['"]default-src\s+\*/gi,
|
|
857
|
+
/sameSite\s*:\s*['"]none['"]\s*,?\s*secure\s*:\s*false/gi
|
|
858
|
+
],
|
|
859
|
+
fixSuggestion: 'Replace wildcard CORS with specific allowed origins. Enable SSL verification. Use restrictive CSP. Set secure cookies.',
|
|
860
|
+
penalty: 'Security misconfiguration',
|
|
861
|
+
languages: ['typescript', 'javascript', 'python']
|
|
862
|
+
},
|
|
863
|
+
// AI-AUDIT-006: TODO/FIXME Compliance Gaps
|
|
864
|
+
{
|
|
865
|
+
id: 'AI-AUDIT-006',
|
|
866
|
+
name: 'Unresolved Compliance TODOs',
|
|
867
|
+
severity: 'low',
|
|
868
|
+
description: 'AI-generated code often includes TODO/FIXME comments for compliance-related features (consent, age verification, privacy policy) that may ship unimplemented.',
|
|
869
|
+
patterns: [
|
|
870
|
+
/\/\/\s*(?:TODO|FIXME|HACK|XXX).*(?:consent|age\s*verif|privacy\s*policy|parental|coppa|gdpr|data\s*retention)/gi,
|
|
871
|
+
/\/\/\s*(?:TODO|FIXME).*(?:implement|add|need|require).*(?:auth|login|permission|access\s*control)/gi,
|
|
872
|
+
/#\s*(?:TODO|FIXME).*(?:consent|age\s*verif|privacy|parental|coppa)/gi
|
|
873
|
+
],
|
|
874
|
+
fixSuggestion: 'Resolve all compliance-related TODOs before shipping. Each unresolved TODO is a potential COPPA violation in production.',
|
|
875
|
+
penalty: 'Unimplemented compliance requirement',
|
|
876
|
+
languages: ['typescript', 'javascript', 'python', 'java', 'swift', 'kotlin']
|
|
877
|
+
}
|
|
878
|
+
];
|
|
879
|
+
// Australia Safety by Design (SbD) Rules
|
|
880
|
+
// Based on the eSafety Commissioner's Safety by Design framework (Online Safety Act 2021).
|
|
881
|
+
// Detects code patterns that violate SbD principles: service provider responsibility,
|
|
882
|
+
// user empowerment, and transparency/accountability — especially for platforms serving minors.
|
|
883
|
+
exports.AU_SBD_RULES = [
|
|
884
|
+
// AU-SBD-001: Default Public Profiles
|
|
885
|
+
{
|
|
886
|
+
id: 'AU-SBD-001',
|
|
887
|
+
name: 'Default Public Profile Visibility',
|
|
888
|
+
severity: 'high',
|
|
889
|
+
description: 'User profiles default to public or visible. AU Safety by Design requires privacy-by-default for minors — profiles should be private until explicitly changed by the user or a verified parent.',
|
|
890
|
+
patterns: [
|
|
891
|
+
/(?:visibility|profile_?visibility|is_?public|isPublic)\s*[:=]\s*(?:['"]public['"]|true)/gi,
|
|
892
|
+
/default(?:_?visibility|_?privacy|_?profile)\s*[:=]\s*['"](?:public|open|visible|everyone)['"]/gi,
|
|
893
|
+
/(?:privacy|profilePrivacy)\s*[:=]\s*\{[^}]*default\s*:\s*['"](?:public|open|everyone)['"]/gi,
|
|
894
|
+
/(?:showProfile|profileVisible|publicByDefault|show_profile)\s*[:=]\s*true/gi,
|
|
895
|
+
/(?:searchable|discoverable|findable)\s*[:=]\s*true\b/gi,
|
|
896
|
+
],
|
|
897
|
+
fixSuggestion: 'Set profile visibility to "private" by default. Require explicit user action (or parental consent for children) to make profiles public. AU SbD Principle 1: safety as a fundamental design consideration.',
|
|
898
|
+
penalty: 'Default public exposure of minor profiles',
|
|
899
|
+
languages: ['typescript', 'javascript', 'python', 'java', 'swift', 'kotlin', 'php', 'csharp']
|
|
900
|
+
},
|
|
901
|
+
// AU-SBD-002: Missing Report/Block Mechanism
|
|
902
|
+
{
|
|
903
|
+
id: 'AU-SBD-002',
|
|
904
|
+
name: 'Social Features Without Report/Block',
|
|
905
|
+
severity: 'medium',
|
|
906
|
+
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.',
|
|
907
|
+
patterns: [
|
|
908
|
+
/(?:addComment|postComment|submitComment|createComment|commentCreate)\s*(?:=|:|\()/gi,
|
|
909
|
+
/(?:sendMessage|createMessage|postMessage|submitMessage|messageCreate)\s*(?:=|:|\()/gi,
|
|
910
|
+
/(?:createPost|submitPost|publishPost|addPost|postCreate)\s*(?:=|:|\()/gi,
|
|
911
|
+
/(?:addReview|submitReview|createReview|postReview|reviewCreate)\s*(?:=|:|\()/gi,
|
|
912
|
+
/(?:shareContent|createShare|submitShare)\s*(?:=|:|\()/gi,
|
|
913
|
+
],
|
|
914
|
+
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.',
|
|
915
|
+
penalty: 'Social features without safety controls',
|
|
916
|
+
languages: ['typescript', 'javascript', 'python', 'java', 'swift', 'kotlin', 'php']
|
|
917
|
+
},
|
|
918
|
+
// AU-SBD-003: Unrestricted Direct Messaging
|
|
919
|
+
{
|
|
920
|
+
id: 'AU-SBD-003',
|
|
921
|
+
name: 'Unrestricted Direct Messaging for Minors',
|
|
922
|
+
severity: 'critical',
|
|
923
|
+
description: 'Direct messaging or chat functionality without safety controls (contact restrictions, message filtering, or parental oversight). The AU Online Safety Act requires platforms to take reasonable steps to prevent child exploitation in private communications.',
|
|
924
|
+
patterns: [
|
|
925
|
+
/(?:directMessage|sendDM|privateMess|createDM|dmChannel|startChat|privateChat|initiateChat)\s*(?:=|:|\()/gi,
|
|
926
|
+
/(?:WebSocket|io\.connect|socket\.emit)\s*\([^)]*(?:chat|message|dm|private)/gi,
|
|
927
|
+
/(?:allowDMs?|enableDMs?|allow_?direct_?message|enable_?private_?message)\s*[:=]\s*true/gi,
|
|
928
|
+
/(?:contactStranger|messageAnyone|openChat|unrestricted_?message)\s*[:=]\s*true/gi,
|
|
929
|
+
],
|
|
930
|
+
fixSuggestion: 'Add safety controls to messaging: restrict contacts to approved friends, implement message filtering, enable parental oversight, and log communications for safety review. AU Online Safety Act 2021 s.45-46.',
|
|
931
|
+
penalty: 'Unrestricted private communication channel',
|
|
932
|
+
languages: ['typescript', 'javascript', 'python', 'java', 'swift', 'kotlin']
|
|
933
|
+
},
|
|
934
|
+
// AU-SBD-004: Algorithmic Feeds Without Safety Guardrails
|
|
935
|
+
{
|
|
936
|
+
id: 'AU-SBD-004',
|
|
937
|
+
name: 'Recommendation Algorithm Without Safety Guardrails',
|
|
938
|
+
severity: 'high',
|
|
939
|
+
description: 'Content recommendation or feed algorithms detected without safety filtering, content classification, or age-appropriate guardrails. AU SbD requires platforms to assess and mitigate algorithmic harms, particularly for young users.',
|
|
940
|
+
patterns: [
|
|
941
|
+
/(?:recommendContent|getRecommendations|suggestContent|personalizedFeed|forYouFeed|contentFeed)\s*(?:=|:|\()/gi,
|
|
942
|
+
/(?:algorithm|algo)(?:_?feed|_?rank|_?recommend|_?suggest)\s*(?:=|:|\()/gi,
|
|
943
|
+
/(?:trending|viral|popular)(?:_?content|_?posts|_?feed|_?items)\s*(?:=|:|\()/gi,
|
|
944
|
+
/(?:engagement_?score|clickBait|engagement_?rank|watch_?next|autoplay_?next)\s*[:=]/gi,
|
|
945
|
+
/(?:rabbit_?hole|endless_?feed|infinite_?recommend|auto_?suggest)\s*[:=]\s*true/gi,
|
|
946
|
+
],
|
|
947
|
+
fixSuggestion: 'Add age-appropriate content filters to recommendation algorithms. Classify content before serving, implement safety guardrails, and provide transparency on how content is selected. AU SbD Principle 3: transparency and accountability.',
|
|
948
|
+
penalty: 'Unfiltered algorithmic content delivery',
|
|
949
|
+
languages: ['typescript', 'javascript', 'python', 'java', 'swift', 'kotlin']
|
|
950
|
+
},
|
|
951
|
+
// AU-SBD-005: Missing Digital Wellbeing / Screen Time Controls
|
|
952
|
+
{
|
|
953
|
+
id: 'AU-SBD-005',
|
|
954
|
+
name: 'Engagement Features Without Time Awareness',
|
|
955
|
+
severity: 'medium',
|
|
956
|
+
description: 'High-engagement features (autoplay, continuous scrolling, notifications) detected without corresponding digital wellbeing controls (screen time limits, break reminders, usage dashboards). AU SbD encourages platforms to build in digital wellbeing tools.',
|
|
957
|
+
patterns: [
|
|
958
|
+
/(?:autoplay|auto_?play)\s*[:=]\s*true/gi,
|
|
959
|
+
/(?:autoPlay|autoPlayNext|playNext|nextEpisode)\s*[:=]\s*true/gi,
|
|
960
|
+
/(?:continuous_?play|binge_?mode|marathon_?mode|watch_?party)\s*[:=]\s*true/gi,
|
|
961
|
+
/(?:push_?notification|sendNotification|scheduleNotif|notif_?trigger).*(?:re_?engage|comeback|miss_?you|inactive)/gi,
|
|
962
|
+
/(?:daily_?reward|login_?bonus|daily_?streak|come_?back_?reward)\s*(?:=|:|\()/gi,
|
|
963
|
+
],
|
|
964
|
+
fixSuggestion: 'Implement digital wellbeing features: screen time dashboards, break reminders after sustained use, and configurable usage limits (especially for accounts under 18). AU SbD: promote healthy technology use.',
|
|
965
|
+
penalty: 'Engagement-maximizing features without wellbeing controls',
|
|
966
|
+
languages: ['typescript', 'javascript', 'python', 'java', 'swift', 'kotlin']
|
|
967
|
+
},
|
|
968
|
+
// AU-SBD-006: Location Sharing Without Explicit Opt-In
|
|
969
|
+
{
|
|
970
|
+
id: 'AU-SBD-006',
|
|
971
|
+
name: 'Location Data Without Explicit Consent',
|
|
972
|
+
severity: 'critical',
|
|
973
|
+
description: 'Location data collection or sharing enabled without explicit, informed opt-in. AU SbD and the Privacy Act 1988 require data minimization, especially for children\'s geolocation data — location should never be collected by default.',
|
|
974
|
+
patterns: [
|
|
975
|
+
/(?:shareLocation|share_?location|locationSharing|broadcastLocation)\s*[:=]\s*true/gi,
|
|
976
|
+
/(?:location_?visible|show_?location|display_?location|location_?public)\s*[:=]\s*true/gi,
|
|
977
|
+
/(?:trackLocation|location_?tracking|geo_?tracking|locationTracker)\s*[:=]\s*true/gi,
|
|
978
|
+
/navigator\.geolocation\.(?:watchPosition|getCurrentPosition)\s*\(/gi,
|
|
979
|
+
/(?:CLLocationManager|LocationManager|FusedLocationProvider).*(?:startUpdating|requestLocation|requestLocationUpdates)/gi,
|
|
980
|
+
],
|
|
981
|
+
fixSuggestion: 'Require explicit opt-in for any location data collection. Never enable location sharing by default. For minors, require parental consent before any geolocation access. AU Privacy Act 1988 APP 3.2.',
|
|
982
|
+
penalty: 'Default location data exposure',
|
|
983
|
+
languages: ['typescript', 'javascript', 'python', 'java', 'swift', 'kotlin']
|
|
984
|
+
}
|
|
985
|
+
];
|
|
690
986
|
/**
|
|
691
987
|
* Parse a .haloignore file content
|
|
692
988
|
*
|
|
@@ -792,6 +1088,14 @@ class HaloEngine {
|
|
|
792
1088
|
if (config.ethical) {
|
|
793
1089
|
this.rules = [...this.rules, ...exports.ETHICAL_RULES];
|
|
794
1090
|
}
|
|
1091
|
+
// Add AI-Generated Code Audit Rules if enabled (P2.5)
|
|
1092
|
+
if (config.aiAudit) {
|
|
1093
|
+
this.rules = [...this.rules, ...exports.AI_AUDIT_RULES];
|
|
1094
|
+
}
|
|
1095
|
+
// Add Australia Safety by Design sector rules if enabled (P3-3)
|
|
1096
|
+
if (config.sectorAuSbd) {
|
|
1097
|
+
this.rules = [...this.rules, ...exports.AU_SBD_RULES];
|
|
1098
|
+
}
|
|
795
1099
|
if (config.severityFilter) {
|
|
796
1100
|
this.rules = this.rules.filter(r => config.severityFilter.includes(r.severity));
|
|
797
1101
|
}
|
|
@@ -916,7 +1220,8 @@ class HaloEngine {
|
|
|
916
1220
|
const trimmedLine = lineContent.trim();
|
|
917
1221
|
if (trimmedLine.startsWith('//') || trimmedLine.startsWith('/*') || trimmedLine.startsWith('*') || trimmedLine.startsWith('<!--') || trimmedLine.startsWith('#')) {
|
|
918
1222
|
// Exception: don't skip halo-ignore comments (those are suppression directives)
|
|
919
|
-
|
|
1223
|
+
// Exception: AI-AUDIT-006 intentionally scans comments for compliance TODOs
|
|
1224
|
+
if (!trimmedLine.includes('halo-ignore') && rule.id !== 'AI-AUDIT-006') {
|
|
920
1225
|
continue;
|
|
921
1226
|
}
|
|
922
1227
|
}
|
|
@@ -1036,5 +1341,12 @@ Object.defineProperty(exports, "transformSetDefault", { enumerable: true, get: f
|
|
|
1036
1341
|
// Re-export compliance score engine (Sprint 5 Track 3)
|
|
1037
1342
|
var scoring_1 = require("./scoring");
|
|
1038
1343
|
Object.defineProperty(exports, "ComplianceScoreEngine", { enumerable: true, get: function () { return scoring_1.ComplianceScoreEngine; } });
|
|
1344
|
+
// Re-export scaffold engine (Sprint 5 P2)
|
|
1345
|
+
var scaffold_engine_1 = require("./scaffold-engine");
|
|
1346
|
+
Object.defineProperty(exports, "ScaffoldEngine", { enumerable: true, get: function () { return scaffold_engine_1.ScaffoldEngine; } });
|
|
1347
|
+
var framework_detect_1 = require("./framework-detect");
|
|
1348
|
+
Object.defineProperty(exports, "detectFramework", { enumerable: true, get: function () { return framework_detect_1.detectFramework; } });
|
|
1349
|
+
var index_1 = require("./scaffolds/index");
|
|
1350
|
+
Object.defineProperty(exports, "SCAFFOLD_REGISTRY", { enumerable: true, get: function () { return index_1.SCAFFOLD_REGISTRY; } });
|
|
1039
1351
|
exports.default = HaloEngine;
|
|
1040
1352
|
//# sourceMappingURL=index.js.map
|