@ash-technologia/fathom 0.1.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/CHANGELOG.md +62 -0
- package/LICENSE +21 -0
- package/README.md +725 -0
- package/dist/analyzers/architecture/graph.d.ts +29 -0
- package/dist/analyzers/architecture/graph.d.ts.map +1 -0
- package/dist/analyzers/architecture/graph.js +347 -0
- package/dist/analyzers/architecture/graph.js.map +1 -0
- package/dist/analyzers/architecture/imports.d.ts +24 -0
- package/dist/analyzers/architecture/imports.d.ts.map +1 -0
- package/dist/analyzers/architecture/imports.js +155 -0
- package/dist/analyzers/architecture/imports.js.map +1 -0
- package/dist/analyzers/architecture/index.d.ts +13 -0
- package/dist/analyzers/architecture/index.d.ts.map +1 -0
- package/dist/analyzers/architecture/index.js +217 -0
- package/dist/analyzers/architecture/index.js.map +1 -0
- package/dist/analyzers/architecture/types.d.ts +65 -0
- package/dist/analyzers/architecture/types.d.ts.map +1 -0
- package/dist/analyzers/architecture/types.js +2 -0
- package/dist/analyzers/architecture/types.js.map +1 -0
- package/dist/analyzers/cicd/index.d.ts +11 -0
- package/dist/analyzers/cicd/index.d.ts.map +1 -0
- package/dist/analyzers/cicd/index.js +157 -0
- package/dist/analyzers/cicd/index.js.map +1 -0
- package/dist/analyzers/dependencies/index.d.ts +11 -0
- package/dist/analyzers/dependencies/index.d.ts.map +1 -0
- package/dist/analyzers/dependencies/index.js +403 -0
- package/dist/analyzers/dependencies/index.js.map +1 -0
- package/dist/analyzers/dependencies/lockfiles.d.ts +19 -0
- package/dist/analyzers/dependencies/lockfiles.d.ts.map +1 -0
- package/dist/analyzers/dependencies/lockfiles.js +172 -0
- package/dist/analyzers/dependencies/lockfiles.js.map +1 -0
- package/dist/analyzers/dependencies/online.d.ts +19 -0
- package/dist/analyzers/dependencies/online.d.ts.map +1 -0
- package/dist/analyzers/dependencies/online.js +116 -0
- package/dist/analyzers/dependencies/online.js.map +1 -0
- package/dist/analyzers/dependencies/types.d.ts +49 -0
- package/dist/analyzers/dependencies/types.d.ts.map +1 -0
- package/dist/analyzers/dependencies/types.js +2 -0
- package/dist/analyzers/dependencies/types.js.map +1 -0
- package/dist/analyzers/documentation/index.d.ts +11 -0
- package/dist/analyzers/documentation/index.d.ts.map +1 -0
- package/dist/analyzers/documentation/index.js +166 -0
- package/dist/analyzers/documentation/index.js.map +1 -0
- package/dist/analyzers/git/index.d.ts +11 -0
- package/dist/analyzers/git/index.d.ts.map +1 -0
- package/dist/analyzers/git/index.js +180 -0
- package/dist/analyzers/git/index.js.map +1 -0
- package/dist/analyzers/project/index.d.ts +11 -0
- package/dist/analyzers/project/index.d.ts.map +1 -0
- package/dist/analyzers/project/index.js +95 -0
- package/dist/analyzers/project/index.js.map +1 -0
- package/dist/analyzers/quality/index.d.ts +11 -0
- package/dist/analyzers/quality/index.d.ts.map +1 -0
- package/dist/analyzers/quality/index.js +198 -0
- package/dist/analyzers/quality/index.js.map +1 -0
- package/dist/analyzers/security/index.d.ts +11 -0
- package/dist/analyzers/security/index.d.ts.map +1 -0
- package/dist/analyzers/security/index.js +236 -0
- package/dist/analyzers/security/index.js.map +1 -0
- package/dist/analyzers/testing/index.d.ts +11 -0
- package/dist/analyzers/testing/index.d.ts.map +1 -0
- package/dist/analyzers/testing/index.js +134 -0
- package/dist/analyzers/testing/index.js.map +1 -0
- package/dist/baseline/baseline.d.ts +20 -0
- package/dist/baseline/baseline.d.ts.map +1 -0
- package/dist/baseline/baseline.js +134 -0
- package/dist/baseline/baseline.js.map +1 -0
- package/dist/baseline/compare.d.ts +14 -0
- package/dist/baseline/compare.d.ts.map +1 -0
- package/dist/baseline/compare.js +113 -0
- package/dist/baseline/compare.js.map +1 -0
- package/dist/baseline/types.d.ts +25 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +2 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/cli/commands.d.ts +39 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +277 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +29 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/options.d.ts +55 -0
- package/dist/cli/options.d.ts.map +1 -0
- package/dist/cli/options.js +258 -0
- package/dist/cli/options.js.map +1 -0
- package/dist/core/analyzer.d.ts +33 -0
- package/dist/core/analyzer.d.ts.map +1 -0
- package/dist/core/analyzer.js +19 -0
- package/dist/core/analyzer.js.map +1 -0
- package/dist/core/context.d.ts +90 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +159 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/errors.d.ts +74 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +109 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/findings.d.ts +59 -0
- package/dist/core/findings.d.ts.map +1 -0
- package/dist/core/findings.js +34 -0
- package/dist/core/findings.js.map +1 -0
- package/dist/core/orchestrator.d.ts +44 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +160 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/result.d.ts +169 -0
- package/dist/core/result.d.ts.map +1 -0
- package/dist/core/result.js +2 -0
- package/dist/core/result.js.map +1 -0
- package/dist/detectors/framework.d.ts +22 -0
- package/dist/detectors/framework.d.ts.map +1 -0
- package/dist/detectors/framework.js +147 -0
- package/dist/detectors/framework.js.map +1 -0
- package/dist/detectors/language.d.ts +13 -0
- package/dist/detectors/language.d.ts.map +1 -0
- package/dist/detectors/language.js +100 -0
- package/dist/detectors/language.js.map +1 -0
- package/dist/detectors/package-manager.d.ts +17 -0
- package/dist/detectors/package-manager.d.ts.map +1 -0
- package/dist/detectors/package-manager.js +67 -0
- package/dist/detectors/package-manager.js.map +1 -0
- package/dist/detectors/project-type.d.ts +20 -0
- package/dist/detectors/project-type.d.ts.map +1 -0
- package/dist/detectors/project-type.js +48 -0
- package/dist/detectors/project-type.js.map +1 -0
- package/dist/diff/analyzer.d.ts +16 -0
- package/dist/diff/analyzer.d.ts.map +1 -0
- package/dist/diff/analyzer.js +296 -0
- package/dist/diff/analyzer.js.map +1 -0
- package/dist/diff/git-diff.d.ts +44 -0
- package/dist/diff/git-diff.d.ts.map +1 -0
- package/dist/diff/git-diff.js +256 -0
- package/dist/diff/git-diff.js.map +1 -0
- package/dist/diff/types.d.ts +75 -0
- package/dist/diff/types.d.ts.map +1 -0
- package/dist/diff/types.js +2 -0
- package/dist/diff/types.js.map +1 -0
- package/dist/integrations/github/context.d.ts +22 -0
- package/dist/integrations/github/context.d.ts.map +1 -0
- package/dist/integrations/github/context.js +115 -0
- package/dist/integrations/github/context.js.map +1 -0
- package/dist/integrations/github/types.d.ts +30 -0
- package/dist/integrations/github/types.d.ts.map +1 -0
- package/dist/integrations/github/types.js +2 -0
- package/dist/integrations/github/types.js.map +1 -0
- package/dist/plugins/context.d.ts +13 -0
- package/dist/plugins/context.d.ts.map +1 -0
- package/dist/plugins/context.js +102 -0
- package/dist/plugins/context.js.map +1 -0
- package/dist/plugins/examples/react.d.ts +8 -0
- package/dist/plugins/examples/react.d.ts.map +1 -0
- package/dist/plugins/examples/react.js +129 -0
- package/dist/plugins/examples/react.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +4 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/registry.d.ts +45 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +236 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/plugins/types.d.ts +148 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +2 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/reporters/html.d.ts +11 -0
- package/dist/reporters/html.d.ts.map +1 -0
- package/dist/reporters/html.js +1352 -0
- package/dist/reporters/html.js.map +1 -0
- package/dist/reporters/json.d.ts +10 -0
- package/dist/reporters/json.d.ts.map +1 -0
- package/dist/reporters/json.js +11 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/markdown.d.ts +7 -0
- package/dist/reporters/markdown.d.ts.map +1 -0
- package/dist/reporters/markdown.js +74 -0
- package/dist/reporters/markdown.js.map +1 -0
- package/dist/reporters/sarif.d.ts +133 -0
- package/dist/reporters/sarif.d.ts.map +1 -0
- package/dist/reporters/sarif.js +268 -0
- package/dist/reporters/sarif.js.map +1 -0
- package/dist/reporters/terminal.d.ts +17 -0
- package/dist/reporters/terminal.d.ts.map +1 -0
- package/dist/reporters/terminal.js +423 -0
- package/dist/reporters/terminal.js.map +1 -0
- package/dist/rules/categories.d.ts +8 -0
- package/dist/rules/categories.d.ts.map +1 -0
- package/dist/rules/categories.js +23 -0
- package/dist/rules/categories.js.map +1 -0
- package/dist/rules/definitions.d.ts +25 -0
- package/dist/rules/definitions.d.ts.map +1 -0
- package/dist/rules/definitions.js +429 -0
- package/dist/rules/definitions.js.map +1 -0
- package/dist/rules/registry.d.ts +36 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +64 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/rules/severity.d.ts +13 -0
- package/dist/rules/severity.d.ts.map +1 -0
- package/dist/rules/severity.js +15 -0
- package/dist/rules/severity.js.map +1 -0
- package/dist/scoring/confidence.d.ts +12 -0
- package/dist/scoring/confidence.d.ts.map +1 -0
- package/dist/scoring/confidence.js +22 -0
- package/dist/scoring/confidence.js.map +1 -0
- package/dist/scoring/score.d.ts +27 -0
- package/dist/scoring/score.d.ts.map +1 -0
- package/dist/scoring/score.js +81 -0
- package/dist/scoring/score.js.map +1 -0
- package/dist/scoring/weights.d.ts +11 -0
- package/dist/scoring/weights.d.ts.map +1 -0
- package/dist/scoring/weights.js +26 -0
- package/dist/scoring/weights.js.map +1 -0
- package/dist/utils/filesystem.d.ts +65 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +299 -0
- package/dist/utils/filesystem.js.map +1 -0
- package/dist/utils/git.d.ts +47 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +125 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +25 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +30 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +48 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/timing.d.ts +12 -0
- package/dist/utils/timing.d.ts.map +1 -0
- package/dist/utils/timing.js +19 -0
- package/dist/utils/timing.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createFindingId } from '../../core/findings.js';
|
|
2
|
+
import { createTimer } from '../../utils/timing.js';
|
|
3
|
+
export class ProjectAnalyzer {
|
|
4
|
+
id = 'project';
|
|
5
|
+
name = 'Project';
|
|
6
|
+
category = 'project';
|
|
7
|
+
description = 'Detects ecosystems, frameworks, package managers, and repository size.';
|
|
8
|
+
async analyze(context) {
|
|
9
|
+
const elapsed = createTimer();
|
|
10
|
+
const findings = [];
|
|
11
|
+
const warnings = [];
|
|
12
|
+
const sourceFiles = context.files.filter((f) => f.isSource);
|
|
13
|
+
const testFiles = context.files.filter((f) => f.isTest);
|
|
14
|
+
const totalSizeMb = +(context.metadata.totalSizeBytes / (1024 * 1024)).toFixed(2);
|
|
15
|
+
const metrics = {
|
|
16
|
+
totalFiles: context.files.length,
|
|
17
|
+
sourceFiles: sourceFiles.length,
|
|
18
|
+
testFiles: testFiles.length,
|
|
19
|
+
totalSizeMb,
|
|
20
|
+
languages: context.languages.map((l) => l.name),
|
|
21
|
+
frameworks: context.frameworks.map((f) => f.name),
|
|
22
|
+
packageManagers: context.packageManagers.map((pm) => pm.name),
|
|
23
|
+
projectType: context.projectType.type,
|
|
24
|
+
};
|
|
25
|
+
// PROJ-001: Ecosystem (informational)
|
|
26
|
+
if (context.languages.length === 0) {
|
|
27
|
+
findings.push({
|
|
28
|
+
id: createFindingId('PROJ-001', undefined, undefined),
|
|
29
|
+
ruleId: 'PROJ-001',
|
|
30
|
+
category: 'project',
|
|
31
|
+
severity: 'info',
|
|
32
|
+
title: 'No recognized ecosystem detected',
|
|
33
|
+
description: 'Fathom could not detect a primary programming ecosystem.',
|
|
34
|
+
recommendation: 'Verify that source files are present and not excluded.',
|
|
35
|
+
confidence: 0.8,
|
|
36
|
+
autoFixable: false,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// PROJ-002: Frameworks detected
|
|
40
|
+
if (context.frameworks.length > 0) {
|
|
41
|
+
const fwNames = context.frameworks.map((f) => f.name).join(', ');
|
|
42
|
+
findings.push({
|
|
43
|
+
id: createFindingId('PROJ-002', undefined, undefined),
|
|
44
|
+
ruleId: 'PROJ-002',
|
|
45
|
+
category: 'project',
|
|
46
|
+
severity: 'info',
|
|
47
|
+
title: `Frameworks detected: ${fwNames}`,
|
|
48
|
+
description: `Detected frameworks/libraries: ${fwNames}.`,
|
|
49
|
+
recommendation: 'Keep framework dependencies up to date with security releases.',
|
|
50
|
+
confidence: 0.95,
|
|
51
|
+
autoFixable: false,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// PROJ-003: Package managers detected
|
|
55
|
+
if (context.packageManagers.length > 0) {
|
|
56
|
+
const pmNames = context.packageManagers.map((p) => p.name).join(', ');
|
|
57
|
+
findings.push({
|
|
58
|
+
id: createFindingId('PROJ-003', undefined, undefined),
|
|
59
|
+
ruleId: 'PROJ-003',
|
|
60
|
+
category: 'project',
|
|
61
|
+
severity: 'info',
|
|
62
|
+
title: `Package manager detected: ${pmNames}`,
|
|
63
|
+
description: `Detected package manager(s): ${pmNames}.`,
|
|
64
|
+
recommendation: 'Ensure consistent package manager usage across your team.',
|
|
65
|
+
confidence: 0.95,
|
|
66
|
+
autoFixable: false,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// PROJ-004: Large repository notice
|
|
70
|
+
if (context.files.length > 10000) {
|
|
71
|
+
findings.push({
|
|
72
|
+
id: createFindingId('PROJ-004', undefined, undefined),
|
|
73
|
+
ruleId: 'PROJ-004',
|
|
74
|
+
category: 'project',
|
|
75
|
+
severity: 'info',
|
|
76
|
+
title: 'Large repository',
|
|
77
|
+
description: `This repository contains ${context.files.length.toLocaleString()} files (${totalSizeMb} MB). Analysis may be slower.`,
|
|
78
|
+
recommendation: 'Consider reviewing .fathomignore to exclude generated directories.',
|
|
79
|
+
confidence: 0.99,
|
|
80
|
+
autoFixable: false,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
analyzerId: this.id,
|
|
85
|
+
analyzerName: this.name,
|
|
86
|
+
category: this.category,
|
|
87
|
+
status: 'success',
|
|
88
|
+
findings,
|
|
89
|
+
metrics,
|
|
90
|
+
durationMs: elapsed(),
|
|
91
|
+
warnings,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/project/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Analyzer } from '../../core/analyzer.js';
|
|
2
|
+
import type { RepositoryContext } from '../../core/context.js';
|
|
3
|
+
import type { AnalyzerResult } from '../../core/result.js';
|
|
4
|
+
export declare class QualityAnalyzer implements Analyzer {
|
|
5
|
+
readonly id = "quality";
|
|
6
|
+
readonly name = "Code Quality";
|
|
7
|
+
readonly category: "quality";
|
|
8
|
+
readonly description = "Scans source files for maintainability signals: TODOs, debug statements, large files, and empty catch blocks.";
|
|
9
|
+
analyze(context: RepositoryContext): Promise<AnalyzerResult>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/quality/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAuCpE,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,QAAQ,CAAC,EAAE,aAAa;IACxB,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;IACvC,QAAQ,CAAC,WAAW,mHAC8F;IAE5G,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CAmLnE"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { createFindingId } from '../../core/findings.js';
|
|
2
|
+
import { createTimer } from '../../utils/timing.js';
|
|
3
|
+
import { readFileSafe } from '../../utils/filesystem.js';
|
|
4
|
+
import { computeDepth } from '../../utils/paths.js';
|
|
5
|
+
const COMMENT_TODO_PATTERN = /(?:\/\/|#)\s*TODO\b|^\s*(?:\/\*|\*)\s*TODO\b/i;
|
|
6
|
+
const COMMENT_FIXME_PATTERN = /(?:\/\/|#)\s*FIXME\b|^\s*(?:\/\*|\*)\s*FIXME\b/i;
|
|
7
|
+
/** Debug statement patterns for JS/TS */
|
|
8
|
+
const DEBUG_PATTERNS_JS = [
|
|
9
|
+
/\bconsole\.(log|debug|info|warn|error|trace|dir)\s*\(/,
|
|
10
|
+
/\bdebugger\b/,
|
|
11
|
+
/\bdd\s*\(/, // Laravel dd()
|
|
12
|
+
/\bvar_dump\s*\(/, // PHP
|
|
13
|
+
];
|
|
14
|
+
/** Debug statement patterns for Python only */
|
|
15
|
+
const DEBUG_PATTERNS_PYTHON = [
|
|
16
|
+
/\bpdb\.set_trace\(\)/,
|
|
17
|
+
/\bbreakpoint\(\)/,
|
|
18
|
+
/\bprint\s*\(/, // print() is a debug signal in Python source (not test/script) files
|
|
19
|
+
];
|
|
20
|
+
/** Empty catch block patterns */
|
|
21
|
+
const EMPTY_CATCH_PATTERNS = [
|
|
22
|
+
// JS/TS: catch (e) {} or catch {}
|
|
23
|
+
/catch\s*\([^)]*\)\s*\{\s*\}/,
|
|
24
|
+
/catch\s*\{\s*\}/,
|
|
25
|
+
// Python: except: pass or except Exception: pass
|
|
26
|
+
/except\s*[^:]*:\s*\n\s*pass\s*\n/,
|
|
27
|
+
// Java: catch (Exception e) {}
|
|
28
|
+
/catch\s*\([^)]+\)\s*\{\s*\}/,
|
|
29
|
+
];
|
|
30
|
+
const LARGE_FILE_LINE_THRESHOLD = 500;
|
|
31
|
+
const DEEP_NESTING_THRESHOLD = 6;
|
|
32
|
+
export class QualityAnalyzer {
|
|
33
|
+
id = 'quality';
|
|
34
|
+
name = 'Code Quality';
|
|
35
|
+
category = 'quality';
|
|
36
|
+
description = 'Scans source files for maintainability signals: TODOs, debug statements, large files, and empty catch blocks.';
|
|
37
|
+
async analyze(context) {
|
|
38
|
+
const elapsed = createTimer();
|
|
39
|
+
const findings = [];
|
|
40
|
+
const warnings = [];
|
|
41
|
+
let totalTodos = 0;
|
|
42
|
+
let totalFixmes = 0;
|
|
43
|
+
let totalDebugStatements = 0;
|
|
44
|
+
let emptyCatchCount = 0;
|
|
45
|
+
const largeFiles = [];
|
|
46
|
+
const sourceFiles = context.files.filter((f) => f.isSource && !f.isTest);
|
|
47
|
+
for (const file of sourceFiles) {
|
|
48
|
+
if (file.sizeBytes > 2 * 1024 * 1024) {
|
|
49
|
+
warnings.push(`Skipped quality scan for oversized file: ${file.relativePath}`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const content = await readFileSafe(file.absolutePath);
|
|
53
|
+
if (!content)
|
|
54
|
+
continue;
|
|
55
|
+
const lines = content.split('\n');
|
|
56
|
+
// QUAL-003: Debug statements — language-aware
|
|
57
|
+
const isPython = file.extension === '.py' || file.extension === '.pyw';
|
|
58
|
+
const debugPatterns = isPython ? DEBUG_PATTERNS_PYTHON : DEBUG_PATTERNS_JS;
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
// QUAL-001 & QUAL-002: Comment-scoped TODOs and FIXMEs
|
|
61
|
+
if (COMMENT_TODO_PATTERN.test(line)) {
|
|
62
|
+
totalTodos++;
|
|
63
|
+
}
|
|
64
|
+
if (COMMENT_FIXME_PATTERN.test(line)) {
|
|
65
|
+
totalFixmes++;
|
|
66
|
+
}
|
|
67
|
+
// Skip pure comment lines for debug statement scan
|
|
68
|
+
if (/^\s*(\/\/|#|\/\*)/.test(line))
|
|
69
|
+
continue;
|
|
70
|
+
for (const pattern of debugPatterns) {
|
|
71
|
+
if (pattern.test(line)) {
|
|
72
|
+
totalDebugStatements++;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// QUAL-004: Empty catch blocks
|
|
78
|
+
const strippedContent = content
|
|
79
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
80
|
+
.replace(/\/\/.*/g, '')
|
|
81
|
+
.replace(/#.*/g, '');
|
|
82
|
+
for (const pattern of EMPTY_CATCH_PATTERNS) {
|
|
83
|
+
if (pattern.test(strippedContent)) {
|
|
84
|
+
emptyCatchCount++;
|
|
85
|
+
findings.push({
|
|
86
|
+
id: createFindingId('QUAL-004', file.relativePath),
|
|
87
|
+
ruleId: 'QUAL-004',
|
|
88
|
+
category: 'quality',
|
|
89
|
+
severity: 'medium',
|
|
90
|
+
title: `Empty catch block in ${file.relativePath}`,
|
|
91
|
+
description: 'An empty catch block silently swallows errors, making debugging difficult.',
|
|
92
|
+
recommendation: 'Add error handling or at minimum log the error inside the catch block.',
|
|
93
|
+
confidence: 0.75,
|
|
94
|
+
location: { file: file.relativePath },
|
|
95
|
+
autoFixable: false,
|
|
96
|
+
});
|
|
97
|
+
break; // One finding per file
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// QUAL-005: Large files
|
|
101
|
+
if (lines.length > LARGE_FILE_LINE_THRESHOLD) {
|
|
102
|
+
largeFiles.push(file.relativePath);
|
|
103
|
+
findings.push({
|
|
104
|
+
id: createFindingId('QUAL-005', file.relativePath),
|
|
105
|
+
ruleId: 'QUAL-005',
|
|
106
|
+
category: 'quality',
|
|
107
|
+
severity: 'low',
|
|
108
|
+
title: `Large source file: ${file.relativePath}`,
|
|
109
|
+
description: `This file has ${lines.length} lines. Large files can be harder to maintain and test.`,
|
|
110
|
+
recommendation: 'Consider splitting this into smaller, focused modules.',
|
|
111
|
+
confidence: 0.85,
|
|
112
|
+
location: { file: file.relativePath },
|
|
113
|
+
evidence: `${lines.length} lines`,
|
|
114
|
+
autoFixable: false,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// QUAL-001/002: Aggregate findings
|
|
119
|
+
if (totalTodos > 0) {
|
|
120
|
+
findings.push({
|
|
121
|
+
id: createFindingId('QUAL-001'),
|
|
122
|
+
ruleId: 'QUAL-001',
|
|
123
|
+
category: 'quality',
|
|
124
|
+
severity: 'low',
|
|
125
|
+
title: `${totalTodos} TODO comment${totalTodos > 1 ? 's' : ''} found`,
|
|
126
|
+
description: 'TODO comments indicate unfinished work.',
|
|
127
|
+
recommendation: 'Review and resolve TODO comments, or convert them to tracked issues.',
|
|
128
|
+
confidence: 0.99,
|
|
129
|
+
evidence: `${totalTodos} occurrences`,
|
|
130
|
+
autoFixable: false,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (totalFixmes > 0) {
|
|
134
|
+
findings.push({
|
|
135
|
+
id: createFindingId('QUAL-002'),
|
|
136
|
+
ruleId: 'QUAL-002',
|
|
137
|
+
category: 'quality',
|
|
138
|
+
severity: 'low',
|
|
139
|
+
title: `${totalFixmes} FIXME comment${totalFixmes > 1 ? 's' : ''} found`,
|
|
140
|
+
description: 'FIXME comments indicate known broken or problematic code.',
|
|
141
|
+
recommendation: 'Prioritize resolving FIXME comments before shipping.',
|
|
142
|
+
confidence: 0.99,
|
|
143
|
+
evidence: `${totalFixmes} occurrences`,
|
|
144
|
+
autoFixable: false,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (totalDebugStatements > 3) {
|
|
148
|
+
findings.push({
|
|
149
|
+
id: createFindingId('QUAL-003'),
|
|
150
|
+
ruleId: 'QUAL-003',
|
|
151
|
+
category: 'quality',
|
|
152
|
+
severity: 'low',
|
|
153
|
+
title: `${totalDebugStatements} debug statements found`,
|
|
154
|
+
description: 'Debug statements left in production code add noise and can expose sensitive information.',
|
|
155
|
+
recommendation: 'Remove or replace debug statements with proper logging.',
|
|
156
|
+
confidence: 0.75,
|
|
157
|
+
evidence: `${totalDebugStatements} occurrences`,
|
|
158
|
+
autoFixable: false,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// QUAL-006: Deep directory nesting
|
|
162
|
+
const maxDepth = context.files.reduce((max, f) => Math.max(max, computeDepth(f.relativePath)), 0);
|
|
163
|
+
if (maxDepth >= DEEP_NESTING_THRESHOLD) {
|
|
164
|
+
findings.push({
|
|
165
|
+
id: createFindingId('QUAL-006'),
|
|
166
|
+
ruleId: 'QUAL-006',
|
|
167
|
+
category: 'quality',
|
|
168
|
+
severity: 'low',
|
|
169
|
+
title: `Deep directory nesting detected (depth: ${maxDepth})`,
|
|
170
|
+
description: 'Very deep directory structures can make navigation and imports harder.',
|
|
171
|
+
recommendation: 'Consider flattening the directory structure where practical.',
|
|
172
|
+
confidence: 0.7,
|
|
173
|
+
evidence: `Maximum depth: ${maxDepth}`,
|
|
174
|
+
autoFixable: false,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const metrics = {
|
|
178
|
+
todoCount: totalTodos,
|
|
179
|
+
fixmeCount: totalFixmes,
|
|
180
|
+
debugStatements: totalDebugStatements,
|
|
181
|
+
emptyCatchBlocks: emptyCatchCount,
|
|
182
|
+
largeFilesCount: largeFiles.length,
|
|
183
|
+
maxDirectoryDepth: maxDepth,
|
|
184
|
+
sourceFilesScanned: sourceFiles.length,
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
analyzerId: this.id,
|
|
188
|
+
analyzerName: this.name,
|
|
189
|
+
category: this.category,
|
|
190
|
+
status: 'success',
|
|
191
|
+
findings,
|
|
192
|
+
metrics,
|
|
193
|
+
durationMs: elapsed(),
|
|
194
|
+
warnings,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/quality/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Analyzer } from '../../core/analyzer.js';
|
|
2
|
+
import type { RepositoryContext } from '../../core/context.js';
|
|
3
|
+
import type { AnalyzerResult } from '../../core/result.js';
|
|
4
|
+
export declare class SecurityAnalyzer implements Analyzer {
|
|
5
|
+
readonly id = "security";
|
|
6
|
+
readonly name = "Security";
|
|
7
|
+
readonly category: "security";
|
|
8
|
+
readonly description = "Checks for common security hygiene issues including exposed secrets and poor gitignore configuration.";
|
|
9
|
+
analyze(context: RepositoryContext): Promise<AnalyzerResult>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/security/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAyFpE,qBAAa,gBAAiB,YAAW,QAAQ;IAC/C,QAAQ,CAAC,EAAE,cAAc;IACzB,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IACxC,QAAQ,CAAC,WAAW,2GACsF;IAEpG,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CAuKnE"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createFindingId } from '../../core/findings.js';
|
|
3
|
+
import { createTimer } from '../../utils/timing.js';
|
|
4
|
+
import { readFileSafe } from '../../utils/filesystem.js';
|
|
5
|
+
import { isPathMentionedInGitignore } from '../../utils/git.js';
|
|
6
|
+
/**
|
|
7
|
+
* Secret patterns — conservative regexes that minimize false positives.
|
|
8
|
+
* NEVER log the matched value.
|
|
9
|
+
*/
|
|
10
|
+
const SECRET_PATTERNS = [
|
|
11
|
+
// AWS
|
|
12
|
+
{ name: 'AWS Access Key ID', pattern: /AKIA[0-9A-Z]{16}/, confidence: 0.97 },
|
|
13
|
+
{
|
|
14
|
+
name: 'AWS Secret Access Key',
|
|
15
|
+
pattern: /(?:aws[_\-.]?secret[_\-.]?(?:access[_\-.]?)?key|AWS_SECRET_ACCESS_KEY)\s*[:=]\s*["']?[A-Za-z0-9/+]{40}["']?/i,
|
|
16
|
+
confidence: 0.85,
|
|
17
|
+
},
|
|
18
|
+
// GitHub tokens
|
|
19
|
+
{ name: 'GitHub Personal Access Token', pattern: /ghp_[0-9a-zA-Z]{36}/, confidence: 0.99 },
|
|
20
|
+
{ name: 'GitHub OAuth Token', pattern: /gho_[0-9a-zA-Z]{36}/, confidence: 0.99 },
|
|
21
|
+
{ name: 'GitHub App Token', pattern: /ghs_[0-9a-zA-Z]{36}/, confidence: 0.99 },
|
|
22
|
+
// Generic API keys
|
|
23
|
+
{
|
|
24
|
+
name: 'Generic API Key',
|
|
25
|
+
pattern: /(?:api[_\-.]?key|apikey)\s*[:=]\s*["']?[A-Za-z0-9_-]{20,}["']?/i,
|
|
26
|
+
confidence: 0.7,
|
|
27
|
+
},
|
|
28
|
+
// Private keys
|
|
29
|
+
{
|
|
30
|
+
name: 'Private Key Block',
|
|
31
|
+
pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
|
|
32
|
+
confidence: 0.99,
|
|
33
|
+
},
|
|
34
|
+
// Database URLs with credentials
|
|
35
|
+
{
|
|
36
|
+
name: 'Database Connection String',
|
|
37
|
+
pattern: /(?:postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
|
|
38
|
+
confidence: 0.92,
|
|
39
|
+
},
|
|
40
|
+
// Stripe
|
|
41
|
+
{ name: 'Stripe Secret Key', pattern: /sk_(?:live|test)_[0-9a-zA-Z]{24,}/, confidence: 0.99 },
|
|
42
|
+
// Twilio
|
|
43
|
+
{ name: 'Twilio Account SID', pattern: /AC[0-9a-f]{32}/, confidence: 0.8 },
|
|
44
|
+
// Google API Key
|
|
45
|
+
{ name: 'Google API Key', pattern: /AIza[0-9A-Za-z\-_]{35}/, confidence: 0.95 },
|
|
46
|
+
// Slack token
|
|
47
|
+
{ name: 'Slack Token', pattern: /xox[boaprs]-[0-9a-zA-Z-]{10,}/, confidence: 0.95 },
|
|
48
|
+
// Generic "password = value" in config
|
|
49
|
+
{
|
|
50
|
+
name: 'Hardcoded Password',
|
|
51
|
+
pattern: /(?:password|passwd|pwd)\s*[:=]\s*["'][^"']{8,}["']/i,
|
|
52
|
+
confidence: 0.6,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
/**
|
|
56
|
+
* Files that are often credential containers and should not be tracked.
|
|
57
|
+
*/
|
|
58
|
+
const SENSITIVE_FILE_PATTERNS = [
|
|
59
|
+
/^\.env$/,
|
|
60
|
+
/^\.env\..+$/,
|
|
61
|
+
/^\.env\.local$/,
|
|
62
|
+
/^secrets?\.(json|yaml|yml|toml|ini)$/i,
|
|
63
|
+
/^credentials?\.(json|yaml|yml|toml|ini)$/i,
|
|
64
|
+
/^config\/secrets\//,
|
|
65
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* Private key file extensions / names.
|
|
68
|
+
*/
|
|
69
|
+
const PRIVATE_KEY_PATTERNS = [
|
|
70
|
+
/\.pem$/,
|
|
71
|
+
/\.key$/,
|
|
72
|
+
/^id_rsa$/,
|
|
73
|
+
/^id_ecdsa$/,
|
|
74
|
+
/^id_ed25519$/,
|
|
75
|
+
/^id_dsa$/,
|
|
76
|
+
/\.p12$/,
|
|
77
|
+
/\.pfx$/,
|
|
78
|
+
];
|
|
79
|
+
/**
|
|
80
|
+
* URL credential pattern.
|
|
81
|
+
*/
|
|
82
|
+
const URL_CREDENTIAL_PATTERN = /https?:\/\/[^:@\s]+:[^@\s]+@[^\s]+/g;
|
|
83
|
+
export class SecurityAnalyzer {
|
|
84
|
+
id = 'security';
|
|
85
|
+
name = 'Security';
|
|
86
|
+
category = 'security';
|
|
87
|
+
description = 'Checks for common security hygiene issues including exposed secrets and poor gitignore configuration.';
|
|
88
|
+
async analyze(context) {
|
|
89
|
+
const elapsed = createTimer();
|
|
90
|
+
const findings = [];
|
|
91
|
+
const warnings = [];
|
|
92
|
+
let secretPatternCount = 0;
|
|
93
|
+
// SEC-001: .env not ignored
|
|
94
|
+
const envFiles = context.files.filter((f) => /^\.env($|\..+)/.test(path.basename(f.relativePath)));
|
|
95
|
+
for (const envFile of envFiles) {
|
|
96
|
+
const basename = path.basename(envFile.relativePath);
|
|
97
|
+
// Skip example/sample env files
|
|
98
|
+
if (/\.(example|sample|template)$/.test(basename))
|
|
99
|
+
continue;
|
|
100
|
+
const isIgnored = context.git.gitignoreContent
|
|
101
|
+
? isPathMentionedInGitignore(context.git.gitignoreContent, basename) ||
|
|
102
|
+
isPathMentionedInGitignore(context.git.gitignoreContent, '.env')
|
|
103
|
+
: false;
|
|
104
|
+
if (!isIgnored) {
|
|
105
|
+
findings.push({
|
|
106
|
+
id: createFindingId('SEC-001', envFile.relativePath),
|
|
107
|
+
ruleId: 'SEC-001',
|
|
108
|
+
category: 'security',
|
|
109
|
+
severity: 'high',
|
|
110
|
+
title: `Environment file may not be gitignored: ${envFile.relativePath}`,
|
|
111
|
+
description: 'Environment files often contain secrets and should not be committed to Git.',
|
|
112
|
+
recommendation: `Add "${basename}" to .gitignore to prevent accidental commits.`,
|
|
113
|
+
confidence: 0.95,
|
|
114
|
+
location: { file: envFile.relativePath },
|
|
115
|
+
autoFixable: false,
|
|
116
|
+
references: ['https://12factor.net/config'],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// SEC-003: Private key files
|
|
121
|
+
for (const file of context.files) {
|
|
122
|
+
const basename = path.basename(file.relativePath);
|
|
123
|
+
const isPrivateKey = PRIVATE_KEY_PATTERNS.some((p) => p.test(basename));
|
|
124
|
+
if (isPrivateKey && !file.relativePath.includes('node_modules')) {
|
|
125
|
+
findings.push({
|
|
126
|
+
id: createFindingId('SEC-003', file.relativePath),
|
|
127
|
+
ruleId: 'SEC-003',
|
|
128
|
+
category: 'security',
|
|
129
|
+
severity: 'critical',
|
|
130
|
+
title: `Potential private key file: ${file.relativePath}`,
|
|
131
|
+
description: 'This file may contain a private key and should not be committed to the repository.',
|
|
132
|
+
recommendation: 'Add this file to .gitignore and rotate any associated credentials.',
|
|
133
|
+
confidence: 0.88,
|
|
134
|
+
location: { file: file.relativePath },
|
|
135
|
+
autoFixable: false,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// SEC-004: Tracked credential/config files
|
|
140
|
+
for (const file of context.files) {
|
|
141
|
+
const basename = path.basename(file.relativePath).toLowerCase();
|
|
142
|
+
const isSensitive = SENSITIVE_FILE_PATTERNS.some((p) => p.test(basename));
|
|
143
|
+
if (isSensitive) {
|
|
144
|
+
// SEC-001 already handles .env; avoid duplicate
|
|
145
|
+
if (basename.startsWith('.env'))
|
|
146
|
+
continue;
|
|
147
|
+
findings.push({
|
|
148
|
+
id: createFindingId('SEC-004', file.relativePath),
|
|
149
|
+
ruleId: 'SEC-004',
|
|
150
|
+
category: 'security',
|
|
151
|
+
severity: 'high',
|
|
152
|
+
title: `Potentially sensitive config file tracked: ${file.relativePath}`,
|
|
153
|
+
description: 'This file may contain credentials or secrets.',
|
|
154
|
+
recommendation: 'Move secrets to environment variables and add this file to .gitignore.',
|
|
155
|
+
confidence: 0.75,
|
|
156
|
+
location: { file: file.relativePath },
|
|
157
|
+
autoFixable: false,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// SEC-002 and SEC-005: Scan source/config files for secrets
|
|
162
|
+
const filesToScan = context.files.filter((f) => !f.isBinary &&
|
|
163
|
+
(f.isSource || f.isConfig || f.extension === '.yaml' || f.extension === '.yml'));
|
|
164
|
+
for (const file of filesToScan) {
|
|
165
|
+
if (file.sizeBytes > 512 * 1024) {
|
|
166
|
+
warnings.push(`Skipped large file for secret scan: ${file.relativePath}`);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const content = await readFileSafe(file.absolutePath);
|
|
170
|
+
if (!content)
|
|
171
|
+
continue;
|
|
172
|
+
const lines = content.split('\n');
|
|
173
|
+
// SEC-002: Secret patterns
|
|
174
|
+
for (const { name: patternName, pattern, confidence } of SECRET_PATTERNS) {
|
|
175
|
+
let lineIndex = 0;
|
|
176
|
+
for (const line of lines) {
|
|
177
|
+
lineIndex++;
|
|
178
|
+
// Skip comment lines
|
|
179
|
+
if (/^\s*(#|\/\/|\/\*)/.test(line))
|
|
180
|
+
continue;
|
|
181
|
+
if (pattern.test(line)) {
|
|
182
|
+
secretPatternCount++;
|
|
183
|
+
findings.push({
|
|
184
|
+
id: createFindingId('SEC-002', file.relativePath, lineIndex),
|
|
185
|
+
ruleId: 'SEC-002',
|
|
186
|
+
category: 'security',
|
|
187
|
+
severity: 'high',
|
|
188
|
+
title: `Potential credential detected: ${patternName}`,
|
|
189
|
+
description: `A value resembling a ${patternName} was found in a source file.`,
|
|
190
|
+
recommendation: 'Move credentials to environment variables and rotate the compromised value.',
|
|
191
|
+
confidence,
|
|
192
|
+
location: { file: file.relativePath, line: lineIndex },
|
|
193
|
+
// Deliberately no evidence value — never print the secret
|
|
194
|
+
autoFixable: false,
|
|
195
|
+
references: ['https://12factor.net/config'],
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// SEC-005: URL credentials
|
|
201
|
+
const urlMatches = [...content.matchAll(URL_CREDENTIAL_PATTERN)];
|
|
202
|
+
for (const match of urlMatches) {
|
|
203
|
+
// Find the line number
|
|
204
|
+
const lineIndex = content.slice(0, match.index).split('\n').length;
|
|
205
|
+
findings.push({
|
|
206
|
+
id: createFindingId('SEC-005', file.relativePath, lineIndex),
|
|
207
|
+
ruleId: 'SEC-005',
|
|
208
|
+
category: 'security',
|
|
209
|
+
severity: 'high',
|
|
210
|
+
title: 'Credentials embedded in URL',
|
|
211
|
+
description: 'A URL containing embedded credentials was detected.',
|
|
212
|
+
recommendation: 'Use environment variables for usernames and passwords in connection strings.',
|
|
213
|
+
confidence: 0.88,
|
|
214
|
+
location: { file: file.relativePath, line: lineIndex },
|
|
215
|
+
autoFixable: false,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const metrics = {
|
|
220
|
+
envFilesFound: envFiles.length,
|
|
221
|
+
secretPatternsFound: secretPatternCount,
|
|
222
|
+
filesScanned: filesToScan.length,
|
|
223
|
+
};
|
|
224
|
+
return {
|
|
225
|
+
analyzerId: this.id,
|
|
226
|
+
analyzerName: this.name,
|
|
227
|
+
category: this.category,
|
|
228
|
+
status: 'success',
|
|
229
|
+
findings,
|
|
230
|
+
metrics,
|
|
231
|
+
durationMs: elapsed(),
|
|
232
|
+
warnings,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/security/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Analyzer } from '../../core/analyzer.js';
|
|
2
|
+
import type { RepositoryContext } from '../../core/context.js';
|
|
3
|
+
import type { AnalyzerResult } from '../../core/result.js';
|
|
4
|
+
export declare class TestingAnalyzer implements Analyzer {
|
|
5
|
+
readonly id = "testing";
|
|
6
|
+
readonly name = "Testing";
|
|
7
|
+
readonly category: "testing";
|
|
8
|
+
readonly description = "Checks for test infrastructure: directories, test files, and test scripts.";
|
|
9
|
+
analyze(context: RepositoryContext): Promise<AnalyzerResult>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAgCpE,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,QAAQ,CAAC,EAAE,aAAa;IACxB,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;IACvC,QAAQ,CAAC,WAAW,gFAC2D;IAEzE,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CA8GnE"}
|