archrisk-engine 1.0.1 → 1.0.2

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/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export * from './aiDiagnosis.js';
3
3
  export * from './archScanner.js';
4
4
  export * from './repoAnalyzer.js';
5
5
  export * from './deepAnalysis.js';
6
+ export * from './jsAnalyzer.js';
package/dist/index.js CHANGED
@@ -19,3 +19,4 @@ __exportStar(require("./aiDiagnosis.js"), exports);
19
19
  __exportStar(require("./archScanner.js"), exports);
20
20
  __exportStar(require("./repoAnalyzer.js"), exports);
21
21
  __exportStar(require("./deepAnalysis.js"), exports);
22
+ __exportStar(require("./jsAnalyzer.js"), exports);
@@ -0,0 +1,8 @@
1
+ import { AnalysisResult } from './analyzer.js';
2
+ /**
3
+ * [The Eye] JS/TS Analysis Engine
4
+ *
5
+ * RegEx-based static analysis for JavaScript/TypeScript files.
6
+ * MVP: Focused on 8 specific credibility rules.
7
+ */
8
+ export declare function analyzeJsTsCode(code: string, fileName: string): Promise<AnalysisResult>;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeJsTsCode = analyzeJsTsCode;
4
+ /**
5
+ * [The Eye] JS/TS Analysis Engine
6
+ *
7
+ * RegEx-based static analysis for JavaScript/TypeScript files.
8
+ * MVP: Focused on 8 specific credibility rules.
9
+ */
10
+ async function analyzeJsTsCode(code, fileName) {
11
+ // 1. Architecture Health Scan (God Module / Large File)
12
+ const lines = code.split('\n');
13
+ if (lines.length > 800) {
14
+ return {
15
+ hasError: true,
16
+ error: `Large File Risk: ${lines.length} lines. Maintanability risk.`,
17
+ line: 1,
18
+ type: 'ProductionRisk',
19
+ file: fileName
20
+ };
21
+ }
22
+ // 2. Risk Scan (Security & Production Readiness)
23
+ const riskResult = scanForJsRisks(code, fileName, lines);
24
+ if (riskResult.hasError) {
25
+ return riskResult;
26
+ }
27
+ return { hasError: false };
28
+ }
29
+ function scanForJsRisks(code, fileName, lines) {
30
+ const risks = [
31
+ // Critical (🔴) - Security
32
+ {
33
+ pattern: /eval\s*\(|new\s+Function\s*\(/,
34
+ type: 'SecurityRisk',
35
+ message: '[Security] Dynamic code execution detected (eval/new Function). This is a severe security risk.'
36
+ },
37
+ {
38
+ pattern: /child_process\.(exec|execSync)\s*\(/,
39
+ type: 'SecurityRisk',
40
+ message: '[Security] Shell command execution detected. Ensure inputs are sanitized or use spawn without shell.'
41
+ },
42
+ {
43
+ pattern: /spawn\s*\(.*,\s*\{.*shell:\s*true/s, // Multi-line match attempt with DOTALL flag simulated or just simple check
44
+ type: 'SecurityRisk',
45
+ message: '[Security] spawn with { shell: true } detected. This enables shell command injection.'
46
+ },
47
+ {
48
+ pattern: /(?:api[._-]?key|password|secret|token)\s*[:=]\s*['"][a-zA-Z0-9_-]{10,}['"]/i,
49
+ type: 'SecurityRisk',
50
+ message: '[Security] Hardcoded secret detected. Use environment variables.'
51
+ },
52
+ // Warning (🟡) - Production Readiness
53
+ {
54
+ condition: (l) => /(axios(\.[a-z]+)?|fetch|http\.(get|request))\s*\(/.test(l) && !/timeout/.test(l),
55
+ type: 'ProductionRisk',
56
+ message: '[Reliability] HTTP call missing explicit timeout. This can cause cascading failures.'
57
+ },
58
+ // Heuristic: App listen but no global error handler (simplified: check for generic app.use((err... pattern)
59
+ {
60
+ condition: (c) => c.includes('app.listen') && !/app\.use\s*\(\s*\(\s*err/.test(c),
61
+ type: 'ProductionRisk',
62
+ message: '[Reliability] Express app detected but Global Error Handler missing. Unhandled errors may crash the server.'
63
+ },
64
+ {
65
+ pattern: /console\.log\s*\(/,
66
+ type: 'ProductionRisk',
67
+ message: '[Observability] console.log used in production code. Use a structured logger (winston/pino).'
68
+ }
69
+ ];
70
+ // Line-based checks
71
+ for (let i = 0; i < lines.length; i++) {
72
+ const line = lines[i];
73
+ for (const risk of risks) {
74
+ // Pattern check
75
+ if (risk.pattern && risk.pattern.test(line)) {
76
+ // Skip comments for simple cases
77
+ if (line.trim().startsWith('//') || line.trim().startsWith('*'))
78
+ continue;
79
+ return {
80
+ hasError: true,
81
+ error: risk.message,
82
+ line: i + 1,
83
+ type: risk.type,
84
+ file: fileName
85
+ };
86
+ }
87
+ // Line-based Condition check (special case for timeout)
88
+ // We distinguish global vs line condition by context?
89
+ // The 'app.listen' check is clearly global (checks whole code).
90
+ // The 'timeout' check is clearly line based (checks 'l').
91
+ // Let's rely on the message content to know if it is line-based for MVP simplicity
92
+ if (risk.condition && risk.message.includes('HTTP missing timeout') && risk.condition(line)) {
93
+ if (line.trim().startsWith('//') || line.trim().startsWith('*'))
94
+ continue;
95
+ return {
96
+ hasError: true,
97
+ error: risk.message,
98
+ line: i + 1,
99
+ type: risk.type,
100
+ file: fileName
101
+ };
102
+ }
103
+ }
104
+ }
105
+ // File-based checks (Global patterns)
106
+ for (const risk of risks) {
107
+ // Global Condition check
108
+ if (risk.condition && !risk.message.includes('HTTP missing timeout') && risk.condition(code)) {
109
+ return {
110
+ hasError: true,
111
+ error: risk.message,
112
+ line: 0,
113
+ type: risk.type,
114
+ file: fileName
115
+ };
116
+ }
117
+ // Multi-line regex checks
118
+ if (risk.pattern && risk.pattern.flags.includes('s') && risk.pattern.test(code)) {
119
+ return {
120
+ hasError: true,
121
+ error: risk.message,
122
+ line: 0,
123
+ type: risk.type,
124
+ file: fileName
125
+ };
126
+ }
127
+ }
128
+ return { hasError: false };
129
+ }
@@ -39,6 +39,7 @@ const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const child_process_1 = require("child_process");
41
41
  const analyzer_js_1 = require("./analyzer.js");
42
+ const jsAnalyzer_js_1 = require("./jsAnalyzer.js");
42
43
  const archScanner_js_1 = require("./archScanner.js");
43
44
  /**
44
45
  * CEO-ready "Business Translation" for technical risks
@@ -305,6 +306,30 @@ async function analyzeRepository(repoPath, resultsDir) {
305
306
  console.error(`Error analyzing ${f}:`, e);
306
307
  }
307
308
  }
309
+ // 2.1 Code Level Analysis (JS/TS logic)
310
+ for (const f of files.filter(f => /\.(js|ts|jsx|tsx)$/.test(f))) {
311
+ try {
312
+ const code = fs.readFileSync(f, 'utf8');
313
+ const relativePath = path.relative(repoPath, f);
314
+ // Logging Check (Simple console.log check is in rules, but here specific frameworks?)
315
+ // We rely on rules for now.
316
+ const result = await (0, jsAnalyzer_js_1.analyzeJsTsCode)(code, relativePath);
317
+ if (result.hasError) {
318
+ const details = getAuditDetails('RR-SEC-002', result.type || 'Error', result.error || 'Unknown Issue'); // Use SEC-002 for JS? Or reuse.
319
+ findings.push({
320
+ file: relativePath,
321
+ line: result.line || 0,
322
+ type: result.type || 'Error',
323
+ ...details
324
+ });
325
+ if (result.type === 'SecurityRisk')
326
+ criticalCount++;
327
+ }
328
+ }
329
+ catch (e) {
330
+ console.error(`Error analyzing ${f}:`, e);
331
+ }
332
+ }
308
333
  if (!hasLogging && files.filter(f => f.endsWith('.py')).length > 0) {
309
334
  const details = getAuditDetails('RR-LOG-001', 'ProductionRisk', '');
310
335
  findings.push({ file: 'Repository Root', line: 0, type: 'ProductionRisk', ...details });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archrisk-engine",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
package/src/index.ts CHANGED
@@ -3,3 +3,4 @@ export * from './aiDiagnosis.js';
3
3
  export * from './archScanner.js';
4
4
  export * from './repoAnalyzer.js';
5
5
  export * from './deepAnalysis.js';
6
+ export * from './jsAnalyzer.js';
@@ -0,0 +1,143 @@
1
+ import { AnalysisResult } from './analyzer.js';
2
+
3
+ /**
4
+ * [The Eye] JS/TS Analysis Engine
5
+ *
6
+ * RegEx-based static analysis for JavaScript/TypeScript files.
7
+ * MVP: Focused on 8 specific credibility rules.
8
+ */
9
+
10
+ export async function analyzeJsTsCode(code: string, fileName: string): Promise<AnalysisResult> {
11
+ // 1. Architecture Health Scan (God Module / Large File)
12
+ const lines = code.split('\n');
13
+ if (lines.length > 800) {
14
+ return {
15
+ hasError: true,
16
+ error: `Large File Risk: ${lines.length} lines. Maintanability risk.`,
17
+ line: 1,
18
+ type: 'ProductionRisk',
19
+ file: fileName
20
+ };
21
+ }
22
+
23
+ // 2. Risk Scan (Security & Production Readiness)
24
+ const riskResult = scanForJsRisks(code, fileName, lines);
25
+ if (riskResult.hasError) {
26
+ return riskResult;
27
+ }
28
+
29
+ return { hasError: false };
30
+ }
31
+
32
+ interface RiskRule {
33
+ pattern?: RegExp;
34
+ condition?: (codeOrLine: string) => boolean;
35
+ type: string;
36
+ message: string;
37
+ }
38
+
39
+ function scanForJsRisks(code: string, fileName: string, lines: string[]): AnalysisResult {
40
+ const risks: RiskRule[] = [
41
+ // Critical (🔴) - Security
42
+ {
43
+ pattern: /eval\s*\(|new\s+Function\s*\(/,
44
+ type: 'SecurityRisk',
45
+ message: '[Security] Dynamic code execution detected (eval/new Function). This is a severe security risk.'
46
+ },
47
+ {
48
+ pattern: /child_process\.(exec|execSync)\s*\(/,
49
+ type: 'SecurityRisk',
50
+ message: '[Security] Shell command execution detected. Ensure inputs are sanitized or use spawn without shell.'
51
+ },
52
+ {
53
+ pattern: /spawn\s*\(.*,\s*\{.*shell:\s*true/s, // Multi-line match attempt with DOTALL flag simulated or just simple check
54
+ type: 'SecurityRisk',
55
+ message: '[Security] spawn with { shell: true } detected. This enables shell command injection.'
56
+ },
57
+ {
58
+ pattern: /(?:api[._-]?key|password|secret|token)\s*[:=]\s*['"][a-zA-Z0-9_-]{10,}['"]/i,
59
+ type: 'SecurityRisk',
60
+ message: '[Security] Hardcoded secret detected. Use environment variables.'
61
+ },
62
+
63
+ // Warning (🟡) - Production Readiness
64
+ {
65
+ condition: (l) => /(axios(\.[a-z]+)?|fetch|http\.(get|request))\s*\(/.test(l) && !/timeout/.test(l),
66
+ type: 'ProductionRisk',
67
+ message: '[Reliability] HTTP call missing explicit timeout. This can cause cascading failures.'
68
+ },
69
+ // Heuristic: App listen but no global error handler (simplified: check for generic app.use((err... pattern)
70
+ {
71
+ condition: (c) => c.includes('app.listen') && !/app\.use\s*\(\s*\(\s*err/.test(c),
72
+ type: 'ProductionRisk',
73
+ message: '[Reliability] Express app detected but Global Error Handler missing. Unhandled errors may crash the server.'
74
+ },
75
+ {
76
+ pattern: /console\.log\s*\(/,
77
+ type: 'ProductionRisk',
78
+ message: '[Observability] console.log used in production code. Use a structured logger (winston/pino).'
79
+ }
80
+ ];
81
+
82
+ // Line-based checks
83
+ for (let i = 0; i < lines.length; i++) {
84
+ const line = lines[i];
85
+ for (const risk of risks) {
86
+ // Pattern check
87
+ if (risk.pattern && risk.pattern.test(line)) {
88
+ // Skip comments for simple cases
89
+ if (line.trim().startsWith('//') || line.trim().startsWith('*')) continue;
90
+
91
+ return {
92
+ hasError: true,
93
+ error: risk.message,
94
+ line: i + 1,
95
+ type: risk.type,
96
+ file: fileName
97
+ };
98
+ }
99
+
100
+ // Line-based Condition check (special case for timeout)
101
+ // We distinguish global vs line condition by context?
102
+ // The 'app.listen' check is clearly global (checks whole code).
103
+ // The 'timeout' check is clearly line based (checks 'l').
104
+ // Let's rely on the message content to know if it is line-based for MVP simplicity
105
+ if (risk.condition && risk.message.includes('HTTP missing timeout') && risk.condition(line)) {
106
+ if (line.trim().startsWith('//') || line.trim().startsWith('*')) continue;
107
+ return {
108
+ hasError: true,
109
+ error: risk.message,
110
+ line: i + 1,
111
+ type: risk.type,
112
+ file: fileName
113
+ };
114
+ }
115
+ }
116
+ }
117
+
118
+ // File-based checks (Global patterns)
119
+ for (const risk of risks) {
120
+ // Global Condition check
121
+ if (risk.condition && !risk.message.includes('HTTP missing timeout') && risk.condition(code)) {
122
+ return {
123
+ hasError: true,
124
+ error: risk.message,
125
+ line: 0,
126
+ type: risk.type,
127
+ file: fileName
128
+ };
129
+ }
130
+ // Multi-line regex checks
131
+ if (risk.pattern && risk.pattern.flags.includes('s') && risk.pattern.test(code)) {
132
+ return {
133
+ hasError: true,
134
+ error: risk.message,
135
+ line: 0,
136
+ type: risk.type,
137
+ file: fileName
138
+ };
139
+ }
140
+ }
141
+
142
+ return { hasError: false };
143
+ }
@@ -2,6 +2,7 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { execSync } from 'child_process';
4
4
  import { analyzePythonCode, AnalysisResult } from './analyzer.js';
5
+ import { analyzeJsTsCode } from './jsAnalyzer.js';
5
6
  import { depsScan } from './archScanner.js';
6
7
 
7
8
  export interface RepoAnalysisResult {
@@ -314,6 +315,33 @@ export async function analyzeRepository(repoPath: string, resultsDir?: string):
314
315
  }
315
316
  }
316
317
 
318
+ // 2.1 Code Level Analysis (JS/TS logic)
319
+ for (const f of files.filter(f => /\.(js|ts|jsx|tsx)$/.test(f))) {
320
+ try {
321
+ const code = fs.readFileSync(f, 'utf8');
322
+ const relativePath = path.relative(repoPath, f);
323
+
324
+ // Logging Check (Simple console.log check is in rules, but here specific frameworks?)
325
+ // We rely on rules for now.
326
+
327
+ const result = await analyzeJsTsCode(code, relativePath);
328
+
329
+ if (result.hasError) {
330
+ const details = getAuditDetails('RR-SEC-002', result.type || 'Error', result.error || 'Unknown Issue'); // Use SEC-002 for JS? Or reuse.
331
+ findings.push({
332
+ file: relativePath,
333
+ line: result.line || 0,
334
+ type: result.type || 'Error',
335
+ ...details
336
+ });
337
+
338
+ if (result.type === 'SecurityRisk') criticalCount++;
339
+ }
340
+ } catch (e) {
341
+ console.error(`Error analyzing ${f}:`, e);
342
+ }
343
+ }
344
+
317
345
  if (!hasLogging && files.filter(f => f.endsWith('.py')).length > 0) {
318
346
  const details = getAuditDetails('RR-LOG-001', 'ProductionRisk', '');
319
347
  findings.push({ file: 'Repository Root', line: 0, type: 'ProductionRisk', ...details });