vaspera 2.7.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -0
- package/README.md +111 -7
- package/dist/__tests__/agents/adversary/tactics/api.test.d.ts +5 -0
- package/dist/__tests__/agents/adversary/tactics/api.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/api.test.js +369 -0
- package/dist/__tests__/agents/adversary/tactics/api.test.js.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.d.ts +5 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.js +409 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.js.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.d.ts +7 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.js +74 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.js.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.d.ts +7 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.js +374 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.js.map +1 -0
- package/dist/__tests__/compliance-bundle.test.d.ts +9 -0
- package/dist/__tests__/compliance-bundle.test.d.ts.map +1 -0
- package/dist/__tests__/compliance-bundle.test.js +344 -0
- package/dist/__tests__/compliance-bundle.test.js.map +1 -0
- package/dist/__tests__/healthcare-compliance.test.d.ts +9 -0
- package/dist/__tests__/healthcare-compliance.test.d.ts.map +1 -0
- package/dist/__tests__/healthcare-compliance.test.js +233 -0
- package/dist/__tests__/healthcare-compliance.test.js.map +1 -0
- package/dist/action/diff-mode.d.ts +124 -8
- package/dist/action/diff-mode.d.ts.map +1 -1
- package/dist/action/diff-mode.js +384 -65
- package/dist/action/diff-mode.js.map +1 -1
- package/dist/action/diff-mode.test.js +3 -3
- package/dist/action/diff-mode.test.js.map +1 -1
- package/dist/action/pr-comment.test.js +1 -0
- package/dist/action/pr-comment.test.js.map +1 -1
- package/dist/action/sarif-upload.test.js +1 -0
- package/dist/action/sarif-upload.test.js.map +1 -1
- package/dist/agents/adversary/config.d.ts +113 -0
- package/dist/agents/adversary/config.d.ts.map +1 -0
- package/dist/agents/adversary/config.js +391 -0
- package/dist/agents/adversary/config.js.map +1 -0
- package/dist/agents/adversary/index.d.ts +41 -0
- package/dist/agents/adversary/index.d.ts.map +1 -0
- package/dist/agents/adversary/index.js +838 -0
- package/dist/agents/adversary/index.js.map +1 -0
- package/dist/agents/adversary/reporting/compliance-mapper.d.ts +108 -0
- package/dist/agents/adversary/reporting/compliance-mapper.d.ts.map +1 -0
- package/dist/agents/adversary/reporting/compliance-mapper.js +391 -0
- package/dist/agents/adversary/reporting/compliance-mapper.js.map +1 -0
- package/dist/agents/adversary/reporting/index.d.ts +10 -0
- package/dist/agents/adversary/reporting/index.d.ts.map +1 -0
- package/dist/agents/adversary/reporting/index.js +10 -0
- package/dist/agents/adversary/reporting/index.js.map +1 -0
- package/dist/agents/adversary/reporting/poc-generator.d.ts +44 -0
- package/dist/agents/adversary/reporting/poc-generator.d.ts.map +1 -0
- package/dist/agents/adversary/reporting/poc-generator.js +308 -0
- package/dist/agents/adversary/reporting/poc-generator.js.map +1 -0
- package/dist/agents/adversary/tactics/api.d.ts +13 -0
- package/dist/agents/adversary/tactics/api.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/api.js +815 -0
- package/dist/agents/adversary/tactics/api.js.map +1 -0
- package/dist/agents/adversary/tactics/auth.d.ts +13 -0
- package/dist/agents/adversary/tactics/auth.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/auth.js +676 -0
- package/dist/agents/adversary/tactics/auth.js.map +1 -0
- package/dist/agents/adversary/tactics/index.d.ts +129 -0
- package/dist/agents/adversary/tactics/index.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/index.js +199 -0
- package/dist/agents/adversary/tactics/index.js.map +1 -0
- package/dist/agents/adversary/tactics/infra.d.ts +13 -0
- package/dist/agents/adversary/tactics/infra.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/infra.js +827 -0
- package/dist/agents/adversary/tactics/infra.js.map +1 -0
- package/dist/agents/adversary/tactics/injection.d.ts +12 -0
- package/dist/agents/adversary/tactics/injection.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/injection.js +549 -0
- package/dist/agents/adversary/tactics/injection.js.map +1 -0
- package/dist/agents/adversary/tactics/llm.d.ts +13 -0
- package/dist/agents/adversary/tactics/llm.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/llm.js +767 -0
- package/dist/agents/adversary/tactics/llm.js.map +1 -0
- package/dist/agents/adversary/tactics/web-app.d.ts +13 -0
- package/dist/agents/adversary/tactics/web-app.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/web-app.js +717 -0
- package/dist/agents/adversary/tactics/web-app.js.map +1 -0
- package/dist/agents/adversary/types.d.ts +407 -0
- package/dist/agents/adversary/types.d.ts.map +1 -0
- package/dist/agents/adversary/types.js +12 -0
- package/dist/agents/adversary/types.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/zero-day-hunter.d.ts +1 -1
- package/dist/agents/zero-day-hunter.d.ts.map +1 -1
- package/dist/analysis/data-flow.d.ts +154 -0
- package/dist/analysis/data-flow.d.ts.map +1 -0
- package/dist/analysis/data-flow.js +393 -0
- package/dist/analysis/data-flow.js.map +1 -0
- package/dist/analysis/index.d.ts +9 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +9 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/badge-service/index.d.ts +144 -0
- package/dist/badge-service/index.d.ts.map +1 -0
- package/dist/badge-service/index.js +206 -0
- package/dist/badge-service/index.js.map +1 -0
- package/dist/certification/consensus.test.js +2 -0
- package/dist/certification/consensus.test.js.map +1 -1
- package/dist/certification/store.d.ts.map +1 -1
- package/dist/certification/store.js +4 -0
- package/dist/certification/store.js.map +1 -1
- package/dist/certification/types.d.ts +3 -3
- package/dist/certification/types.d.ts.map +1 -1
- package/dist/certification/types.js +2 -0
- package/dist/certification/types.js.map +1 -1
- package/dist/commands/certification/certify.d.ts.map +1 -1
- package/dist/commands/certification/certify.js +18 -4
- package/dist/commands/certification/certify.js.map +1 -1
- package/dist/compliance/attestation.d.ts +39 -0
- package/dist/compliance/attestation.d.ts.map +1 -0
- package/dist/compliance/attestation.js +364 -0
- package/dist/compliance/attestation.js.map +1 -0
- package/dist/compliance/cfr42-part2.d.ts +42 -0
- package/dist/compliance/cfr42-part2.d.ts.map +1 -0
- package/dist/compliance/cfr42-part2.js +408 -0
- package/dist/compliance/cfr42-part2.js.map +1 -0
- package/dist/compliance/compliance-bundle.d.ts +100 -0
- package/dist/compliance/compliance-bundle.d.ts.map +1 -0
- package/dist/compliance/compliance-bundle.js +210 -0
- package/dist/compliance/compliance-bundle.js.map +1 -0
- package/dist/compliance/healthcare-bundle.d.ts +68 -0
- package/dist/compliance/healthcare-bundle.d.ts.map +1 -0
- package/dist/compliance/healthcare-bundle.js +104 -0
- package/dist/compliance/healthcare-bundle.js.map +1 -0
- package/dist/compliance/hipaa.d.ts.map +1 -1
- package/dist/compliance/hipaa.js +14 -11
- package/dist/compliance/hipaa.js.map +1 -1
- package/dist/compliance/index.d.ts +10 -2
- package/dist/compliance/index.d.ts.map +1 -1
- package/dist/compliance/index.js +9 -3
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/mapper.d.ts.map +1 -1
- package/dist/compliance/mapper.js +3 -17
- package/dist/compliance/mapper.js.map +1 -1
- package/dist/compliance/nist-800-53.d.ts +22 -6
- package/dist/compliance/nist-800-53.d.ts.map +1 -1
- package/dist/compliance/nist-800-53.js +264 -272
- package/dist/compliance/nist-800-53.js.map +1 -1
- package/dist/compliance/report.d.ts +31 -2
- package/dist/compliance/report.d.ts.map +1 -1
- package/dist/compliance/report.js +255 -4
- package/dist/compliance/report.js.map +1 -1
- package/dist/compliance/types.d.ts +1 -1
- package/dist/compliance/types.d.ts.map +1 -1
- package/dist/config/flags.d.ts +12 -12
- package/dist/cost/index.d.ts +1 -1
- package/dist/cost/index.d.ts.map +1 -1
- package/dist/cost/index.js +1 -1
- package/dist/cost/index.js.map +1 -1
- package/dist/cost/tracker.d.ts +64 -0
- package/dist/cost/tracker.d.ts.map +1 -1
- package/dist/cost/tracker.js +165 -0
- package/dist/cost/tracker.js.map +1 -1
- package/dist/eval/fixtures/healthcare/audit-gaps.d.ts +28 -0
- package/dist/eval/fixtures/healthcare/audit-gaps.d.ts.map +1 -0
- package/dist/eval/fixtures/healthcare/audit-gaps.js +90 -0
- package/dist/eval/fixtures/healthcare/audit-gaps.js.map +1 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.d.ts +31 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.d.ts.map +1 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.js +61 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.js.map +1 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts +24 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts.map +1 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.js +41 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.js.map +1 -0
- package/dist/evidence/collector.d.ts +21 -0
- package/dist/evidence/collector.d.ts.map +1 -0
- package/dist/evidence/collector.js +340 -0
- package/dist/evidence/collector.js.map +1 -0
- package/dist/evidence/index.d.ts +11 -0
- package/dist/evidence/index.d.ts.map +1 -0
- package/dist/evidence/index.js +12 -0
- package/dist/evidence/index.js.map +1 -0
- package/dist/evidence/store.d.ts +39 -0
- package/dist/evidence/store.d.ts.map +1 -0
- package/dist/evidence/store.js +173 -0
- package/dist/evidence/store.js.map +1 -0
- package/dist/evidence/types.d.ts +175 -0
- package/dist/evidence/types.d.ts.map +1 -0
- package/dist/evidence/types.js +9 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/exporters/checkmarx.d.ts +18 -0
- package/dist/exporters/checkmarx.d.ts.map +1 -0
- package/dist/exporters/checkmarx.js +203 -0
- package/dist/exporters/checkmarx.js.map +1 -0
- package/dist/exporters/index.d.ts +22 -0
- package/dist/exporters/index.d.ts.map +1 -0
- package/dist/exporters/index.js +41 -0
- package/dist/exporters/index.js.map +1 -0
- package/dist/exporters/snyk.d.ts +18 -0
- package/dist/exporters/snyk.d.ts.map +1 -0
- package/dist/exporters/snyk.js +119 -0
- package/dist/exporters/snyk.js.map +1 -0
- package/dist/exporters/sonarqube.d.ts +18 -0
- package/dist/exporters/sonarqube.d.ts.map +1 -0
- package/dist/exporters/sonarqube.js +125 -0
- package/dist/exporters/sonarqube.js.map +1 -0
- package/dist/exporters/types.d.ts +190 -0
- package/dist/exporters/types.d.ts.map +1 -0
- package/dist/exporters/types.js +9 -0
- package/dist/exporters/types.js.map +1 -0
- package/dist/frontier/index.d.ts +12 -0
- package/dist/frontier/index.d.ts.map +1 -0
- package/dist/frontier/index.js +12 -0
- package/dist/frontier/index.js.map +1 -0
- package/dist/frontier/orchestrator.d.ts +73 -0
- package/dist/frontier/orchestrator.d.ts.map +1 -0
- package/dist/frontier/orchestrator.js +312 -0
- package/dist/frontier/orchestrator.js.map +1 -0
- package/dist/frontier/providers/stub.d.ts +32 -0
- package/dist/frontier/providers/stub.d.ts.map +1 -0
- package/dist/frontier/providers/stub.js +66 -0
- package/dist/frontier/providers/stub.js.map +1 -0
- package/dist/frontier/types.d.ts +318 -0
- package/dist/frontier/types.d.ts.map +1 -0
- package/dist/frontier/types.js +27 -0
- package/dist/frontier/types.js.map +1 -0
- package/dist/history/index.d.ts +13 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +15 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/store.d.ts +74 -0
- package/dist/history/store.d.ts.map +1 -0
- package/dist/history/store.js +399 -0
- package/dist/history/store.js.map +1 -0
- package/dist/history/types.d.ts +282 -0
- package/dist/history/types.d.ts.map +1 -0
- package/dist/history/types.js +41 -0
- package/dist/history/types.js.map +1 -0
- package/dist/history/verify.d.ts +44 -0
- package/dist/history/verify.d.ts.map +1 -0
- package/dist/history/verify.js +230 -0
- package/dist/history/verify.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +431 -18
- package/dist/index.js.map +1 -1
- package/dist/multimodel/index.d.ts +1 -0
- package/dist/multimodel/index.d.ts.map +1 -1
- package/dist/multimodel/index.js +2 -0
- package/dist/multimodel/index.js.map +1 -1
- package/dist/multimodel/leaderboard.d.ts +116 -0
- package/dist/multimodel/leaderboard.d.ts.map +1 -0
- package/dist/multimodel/leaderboard.js +262 -0
- package/dist/multimodel/leaderboard.js.map +1 -0
- package/dist/observability/otel.d.ts.map +1 -1
- package/dist/observability/otel.js +1 -3
- package/dist/observability/otel.js.map +1 -1
- package/dist/plugins/loader.js +1 -1
- package/dist/plugins/loader.js.map +1 -1
- package/dist/sbom/provenance.test.js +2 -2
- package/dist/sbom/provenance.test.js.map +1 -1
- package/dist/scanners/agent/agent-chain-analysis.d.ts +152 -0
- package/dist/scanners/agent/agent-chain-analysis.d.ts.map +1 -0
- package/dist/scanners/agent/agent-chain-analysis.js +438 -0
- package/dist/scanners/agent/agent-chain-analysis.js.map +1 -0
- package/dist/scanners/agent/manifest-audit.d.ts.map +1 -1
- package/dist/scanners/agent/manifest-audit.js +30 -18
- package/dist/scanners/agent/manifest-audit.js.map +1 -1
- package/dist/scanners/agent/payloads/index.d.ts +2 -1
- package/dist/scanners/agent/payloads/index.d.ts.map +1 -1
- package/dist/scanners/agent/payloads/index.js +25 -6
- package/dist/scanners/agent/payloads/index.js.map +1 -1
- package/dist/scanners/agent/prompt-injection-fuzzer.d.ts.map +1 -1
- package/dist/scanners/agent/prompt-injection-fuzzer.js +14 -0
- package/dist/scanners/agent/prompt-injection-fuzzer.js.map +1 -1
- package/dist/scanners/agent/types.d.ts +5 -5
- package/dist/scanners/agent/types.d.ts.map +1 -1
- package/dist/scanners/agent/types.js.map +1 -1
- package/dist/scanners/cache.d.ts +156 -0
- package/dist/scanners/cache.d.ts.map +1 -0
- package/dist/scanners/cache.js +462 -0
- package/dist/scanners/cache.js.map +1 -0
- package/dist/scanners/dependencies.d.ts.map +1 -1
- package/dist/scanners/dependencies.js +5 -6
- package/dist/scanners/dependencies.js.map +1 -1
- package/dist/scanners/gosec.d.ts.map +1 -1
- package/dist/scanners/gosec.js +47 -9
- package/dist/scanners/gosec.js.map +1 -1
- package/dist/scanners/healthcare.d.ts +29 -0
- package/dist/scanners/healthcare.d.ts.map +1 -0
- package/dist/scanners/healthcare.js +526 -0
- package/dist/scanners/healthcare.js.map +1 -0
- package/dist/scanners/index.d.ts +1 -0
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +33 -0
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/index.test.js +6 -6
- package/dist/scanners/index.test.js.map +1 -1
- package/dist/scanners/secrets.js +4 -4
- package/dist/scanners/secrets.js.map +1 -1
- package/dist/scanners/semgrep.js +5 -5
- package/dist/scanners/semgrep.js.map +1 -1
- package/dist/scanners/types.d.ts +1 -1
- package/dist/scanners/types.d.ts.map +1 -1
- package/dist/scanners/types.js +1 -0
- package/dist/scanners/types.js.map +1 -1
- package/dist/scanners/typescript.test.js +1 -1
- package/dist/scanners/typescript.test.js.map +1 -1
- package/dist/telemetry/index.d.ts +10 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +10 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/registry.d.ts +178 -0
- package/dist/telemetry/registry.d.ts.map +1 -0
- package/dist/telemetry/registry.js +297 -0
- package/dist/telemetry/registry.js.map +1 -0
- package/dist/telemetry/usage.d.ts +197 -0
- package/dist/telemetry/usage.d.ts.map +1 -0
- package/dist/telemetry/usage.js +244 -0
- package/dist/telemetry/usage.js.map +1 -0
- package/package.json +11 -2
|
@@ -1,20 +1,107 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Differential PR Mode
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Enhanced diff mode for incremental scanning with:
|
|
5
|
+
* - Git-based change detection (git diff --name-only)
|
|
6
|
+
* - Integration with scanner cache
|
|
7
|
+
* - Support for PR base SHA comparison
|
|
8
|
+
* - Incremental certification support
|
|
6
9
|
*
|
|
7
10
|
* @module action/diff-mode
|
|
8
11
|
*/
|
|
9
12
|
import type { ChangedFile, GitHubContext } from "./types.js";
|
|
10
13
|
/**
|
|
11
|
-
*
|
|
14
|
+
* Result of git diff operation
|
|
15
|
+
*/
|
|
16
|
+
export interface GitDiffResult {
|
|
17
|
+
/** Files that changed */
|
|
18
|
+
changedFiles: string[];
|
|
19
|
+
/** Base commit SHA */
|
|
20
|
+
baseSha: string;
|
|
21
|
+
/** Head commit SHA */
|
|
22
|
+
headSha: string;
|
|
23
|
+
/** Whether diff detection was successful */
|
|
24
|
+
success: boolean;
|
|
25
|
+
/** Error message if unsuccessful */
|
|
26
|
+
error?: string;
|
|
27
|
+
/** Total additions */
|
|
28
|
+
additions: number;
|
|
29
|
+
/** Total deletions */
|
|
30
|
+
deletions: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* File info with hash for caching
|
|
34
|
+
*/
|
|
35
|
+
export interface FileInfo {
|
|
36
|
+
/** Relative file path */
|
|
37
|
+
path: string;
|
|
38
|
+
/** SHA-256 hash of file contents */
|
|
39
|
+
hash: string;
|
|
40
|
+
/** File size in bytes */
|
|
41
|
+
size: number;
|
|
42
|
+
/** Last modified time */
|
|
43
|
+
mtime: Date;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Incremental scan configuration
|
|
47
|
+
*/
|
|
48
|
+
export interface IncrementalScanConfig {
|
|
49
|
+
/** Project path */
|
|
50
|
+
projectPath: string;
|
|
51
|
+
/** Base SHA for comparison (e.g., PR base branch) */
|
|
52
|
+
baseSha?: string;
|
|
53
|
+
/** Head SHA (default: HEAD) */
|
|
54
|
+
headSha?: string;
|
|
55
|
+
/** Previous certification ID to build upon */
|
|
56
|
+
previousCertificationId?: string;
|
|
57
|
+
/** Files to force include even if unchanged */
|
|
58
|
+
forceInclude?: string[];
|
|
59
|
+
/** Files to exclude from scanning */
|
|
60
|
+
exclude?: string[];
|
|
61
|
+
/** Whether to scan security-critical files regardless of changes */
|
|
62
|
+
alwaysScanSecurityFiles?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Incremental scan result
|
|
66
|
+
*/
|
|
67
|
+
export interface IncrementalScanResult {
|
|
68
|
+
/** Files that need scanning */
|
|
69
|
+
filesToScan: FileInfo[];
|
|
70
|
+
/** Files that are unchanged (cached) */
|
|
71
|
+
unchangedFiles: string[];
|
|
72
|
+
/** Files that were removed */
|
|
73
|
+
removedFiles: string[];
|
|
74
|
+
/** Git diff information */
|
|
75
|
+
diff: GitDiffResult;
|
|
76
|
+
/** Configuration used */
|
|
77
|
+
config: IncrementalScanConfig;
|
|
78
|
+
/** Whether incremental mode was used */
|
|
79
|
+
isIncremental: boolean;
|
|
80
|
+
/** Estimated time savings from cache */
|
|
81
|
+
estimatedSavingsPercent: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get changed files using git diff
|
|
12
85
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* @
|
|
86
|
+
* Uses `git diff --name-only $BASE_SHA...HEAD` for accurate change detection.
|
|
87
|
+
*
|
|
88
|
+
* @param projectPath - Path to git repository
|
|
89
|
+
* @param baseSha - Base commit SHA (e.g., PR base branch)
|
|
90
|
+
* @param headSha - Head commit SHA (default: HEAD)
|
|
16
91
|
*/
|
|
17
|
-
export declare function
|
|
92
|
+
export declare function getGitDiff(projectPath: string, baseSha?: string, headSha?: string): Promise<GitDiffResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Get file info with content hash
|
|
95
|
+
*/
|
|
96
|
+
export declare function getFileInfo(projectPath: string, filePath: string): Promise<FileInfo | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Get file info for multiple files in parallel
|
|
99
|
+
*/
|
|
100
|
+
export declare function getFilesInfo(projectPath: string, filePaths: string[]): Promise<FileInfo[]>;
|
|
101
|
+
/**
|
|
102
|
+
* Check if a file should be scanned
|
|
103
|
+
*/
|
|
104
|
+
export declare function isScannableFile(filename: string): boolean;
|
|
18
105
|
/**
|
|
19
106
|
* Filter changed files to only scannable ones
|
|
20
107
|
*/
|
|
@@ -23,12 +110,41 @@ export declare function filterScannableFiles(files: ChangedFile[]): ChangedFile[
|
|
|
23
110
|
* Get the list of file paths to scan
|
|
24
111
|
*/
|
|
25
112
|
export declare function getFilesToScan(changedFiles: ChangedFile[]): string[];
|
|
113
|
+
/**
|
|
114
|
+
* Configure incremental scanning
|
|
115
|
+
*
|
|
116
|
+
* Determines which files need scanning based on:
|
|
117
|
+
* 1. Git diff against base SHA
|
|
118
|
+
* 2. File content hashes (for cache validation)
|
|
119
|
+
* 3. Security-critical file detection
|
|
120
|
+
*/
|
|
121
|
+
export declare function configureIncrementalScan(config: IncrementalScanConfig): Promise<IncrementalScanResult>;
|
|
122
|
+
/**
|
|
123
|
+
* Get changed files from GitHub API
|
|
124
|
+
*
|
|
125
|
+
* @param octokit - GitHub API client
|
|
126
|
+
* @param context - GitHub context
|
|
127
|
+
* @returns Array of changed files
|
|
128
|
+
*/
|
|
129
|
+
export declare function getChangedFiles(octokit: any, context: GitHubContext): Promise<ChangedFile[]>;
|
|
26
130
|
/**
|
|
27
131
|
* Check if any security-relevant files changed
|
|
28
132
|
*/
|
|
29
133
|
export declare function hasSecurityRelevantChanges(changedFiles: ChangedFile[]): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Get scan scope recommendation based on changes
|
|
136
|
+
*/
|
|
137
|
+
export declare function getScanScopeRecommendation(changedFiles: ChangedFile[]): "full" | "incremental" | "security-only";
|
|
30
138
|
/**
|
|
31
139
|
* Generate summary of changed files
|
|
32
140
|
*/
|
|
33
141
|
export declare function generateChangeSummary(changedFiles: ChangedFile[]): string;
|
|
142
|
+
/**
|
|
143
|
+
* Group files by directory for parallel processing
|
|
144
|
+
*/
|
|
145
|
+
export declare function groupFilesByDirectory(files: FileInfo[]): Map<string, FileInfo[]>;
|
|
146
|
+
/**
|
|
147
|
+
* Prioritize files for scanning (security-critical first)
|
|
148
|
+
*/
|
|
149
|
+
export declare function prioritizeFiles(files: FileInfo[]): FileInfo[];
|
|
34
150
|
//# sourceMappingURL=diff-mode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-mode.d.ts","sourceRoot":"","sources":["../../src/action/diff-mode.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"diff-mode.d.ts","sourceRoot":"","sources":["../../src/action/diff-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoD7D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,yBAAyB;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,KAAK,EAAE,IAAI,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oEAAoE;IACpE,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,+BAA+B;IAC/B,WAAW,EAAE,QAAQ,EAAE,CAAC;IACxB,wCAAwC;IACxC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,2BAA2B;IAC3B,IAAI,EAAE,aAAa,CAAC;IACpB,yBAAyB;IACzB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,wCAAwC;IACxC,aAAa,EAAE,OAAO,CAAC;IACvB,wCAAwC;IACxC,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,GAAE,MAAe,GACvB,OAAO,CAAC,aAAa,CAAC,CA+ExB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAkB1B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAKrB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAsBzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAMxE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE,CAEpE;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAmEhC;AAkED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAEnC,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CAkExB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,OAAO,CA0B/E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,WAAW,EAAE,GAC1B,MAAM,GAAG,aAAa,GAAG,eAAe,CAkB1C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,CA4CzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,QAAQ,EAAE,GAChB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAWzB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAsB7D"}
|
package/dist/action/diff-mode.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Differential PR Mode
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Enhanced diff mode for incremental scanning with:
|
|
5
|
+
* - Git-based change detection (git diff --name-only)
|
|
6
|
+
* - Integration with scanner cache
|
|
7
|
+
* - Support for PR base SHA comparison
|
|
8
|
+
* - Incremental certification support
|
|
6
9
|
*
|
|
7
10
|
* @module action/diff-mode
|
|
8
11
|
*/
|
|
12
|
+
import { exec } from "child_process";
|
|
13
|
+
import { promisify } from "util";
|
|
14
|
+
import { join, extname } from "path";
|
|
15
|
+
import { stat, readFile } from "fs/promises";
|
|
16
|
+
import { createHash } from "crypto";
|
|
17
|
+
import { logger } from "../logger.js";
|
|
18
|
+
const execAsync = promisify(exec);
|
|
9
19
|
/**
|
|
10
20
|
* File extensions to include in scans
|
|
11
21
|
*/
|
|
@@ -31,7 +41,285 @@ const SCANNABLE_EXTENSIONS = new Set([
|
|
|
31
41
|
".cs",
|
|
32
42
|
".vue",
|
|
33
43
|
".svelte",
|
|
44
|
+
".tf", // Terraform
|
|
45
|
+
".dockerfile", // Docker
|
|
34
46
|
]);
|
|
47
|
+
/**
|
|
48
|
+
* Paths to exclude from scanning
|
|
49
|
+
*/
|
|
50
|
+
const EXCLUDED_PATHS = [
|
|
51
|
+
"node_modules/",
|
|
52
|
+
"dist/",
|
|
53
|
+
"build/",
|
|
54
|
+
"coverage/",
|
|
55
|
+
".git/",
|
|
56
|
+
".vaspera/",
|
|
57
|
+
".next/",
|
|
58
|
+
".turbo/",
|
|
59
|
+
"__pycache__/",
|
|
60
|
+
"vendor/",
|
|
61
|
+
"venv/",
|
|
62
|
+
".venv/",
|
|
63
|
+
];
|
|
64
|
+
/**
|
|
65
|
+
* Get changed files using git diff
|
|
66
|
+
*
|
|
67
|
+
* Uses `git diff --name-only $BASE_SHA...HEAD` for accurate change detection.
|
|
68
|
+
*
|
|
69
|
+
* @param projectPath - Path to git repository
|
|
70
|
+
* @param baseSha - Base commit SHA (e.g., PR base branch)
|
|
71
|
+
* @param headSha - Head commit SHA (default: HEAD)
|
|
72
|
+
*/
|
|
73
|
+
export async function getGitDiff(projectPath, baseSha, headSha = "HEAD") {
|
|
74
|
+
const result = {
|
|
75
|
+
changedFiles: [],
|
|
76
|
+
baseSha: baseSha || "",
|
|
77
|
+
headSha,
|
|
78
|
+
success: false,
|
|
79
|
+
additions: 0,
|
|
80
|
+
deletions: 0,
|
|
81
|
+
};
|
|
82
|
+
try {
|
|
83
|
+
// Get base SHA if not provided (use default branch)
|
|
84
|
+
if (!baseSha) {
|
|
85
|
+
try {
|
|
86
|
+
const { stdout: defaultBranch } = await execAsync('git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed "s@^refs/remotes/origin/@@" || echo "main"', { cwd: projectPath, timeout: 10000 });
|
|
87
|
+
baseSha = `origin/${defaultBranch.trim()}`;
|
|
88
|
+
result.baseSha = baseSha;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
baseSha = "HEAD^";
|
|
92
|
+
result.baseSha = baseSha;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Get list of changed files
|
|
96
|
+
const { stdout: diffOutput } = await execAsync(`git diff --name-only ${baseSha}...${headSha} 2>/dev/null || git diff --name-only ${baseSha} ${headSha}`, { cwd: projectPath, timeout: 30000 });
|
|
97
|
+
result.changedFiles = diffOutput
|
|
98
|
+
.trim()
|
|
99
|
+
.split("\n")
|
|
100
|
+
.filter((f) => f.length > 0);
|
|
101
|
+
// Get stats (additions/deletions)
|
|
102
|
+
try {
|
|
103
|
+
const { stdout: statsOutput } = await execAsync(`git diff --shortstat ${baseSha}...${headSha} 2>/dev/null || git diff --shortstat ${baseSha} ${headSha}`, { cwd: projectPath, timeout: 10000 });
|
|
104
|
+
const addMatch = statsOutput.match(/(\d+) insertions?/);
|
|
105
|
+
const delMatch = statsOutput.match(/(\d+) deletions?/);
|
|
106
|
+
result.additions = addMatch ? parseInt(addMatch[1], 10) : 0;
|
|
107
|
+
result.deletions = delMatch ? parseInt(delMatch[1], 10) : 0;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Stats are optional
|
|
111
|
+
}
|
|
112
|
+
// Get actual head SHA
|
|
113
|
+
try {
|
|
114
|
+
const { stdout: actualHead } = await execAsync(`git rev-parse ${headSha}`, { cwd: projectPath, timeout: 5000 });
|
|
115
|
+
result.headSha = actualHead.trim();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Keep original headSha
|
|
119
|
+
}
|
|
120
|
+
result.success = true;
|
|
121
|
+
logger.info("diff_mode.git_diff", {
|
|
122
|
+
baseSha: result.baseSha,
|
|
123
|
+
headSha: result.headSha,
|
|
124
|
+
changedFiles: result.changedFiles.length,
|
|
125
|
+
additions: result.additions,
|
|
126
|
+
deletions: result.deletions,
|
|
127
|
+
});
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
result.error = String(error);
|
|
132
|
+
logger.warn("diff_mode.git_diff_failed", { error: result.error });
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get file info with content hash
|
|
138
|
+
*/
|
|
139
|
+
export async function getFileInfo(projectPath, filePath) {
|
|
140
|
+
const fullPath = join(projectPath, filePath);
|
|
141
|
+
try {
|
|
142
|
+
const [fileStats, content] = await Promise.all([
|
|
143
|
+
stat(fullPath),
|
|
144
|
+
readFile(fullPath),
|
|
145
|
+
]);
|
|
146
|
+
return {
|
|
147
|
+
path: filePath,
|
|
148
|
+
hash: createHash("sha256").update(content).digest("hex"),
|
|
149
|
+
size: fileStats.size,
|
|
150
|
+
mtime: fileStats.mtime,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get file info for multiple files in parallel
|
|
159
|
+
*/
|
|
160
|
+
export async function getFilesInfo(projectPath, filePaths) {
|
|
161
|
+
const results = await Promise.all(filePaths.map((path) => getFileInfo(projectPath, path)));
|
|
162
|
+
return results.filter((f) => f !== null);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if a file should be scanned
|
|
166
|
+
*/
|
|
167
|
+
export function isScannableFile(filename) {
|
|
168
|
+
// Check excluded paths
|
|
169
|
+
for (const excluded of EXCLUDED_PATHS) {
|
|
170
|
+
if (filename.includes(excluded)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Check extension
|
|
175
|
+
const ext = extname(filename).toLowerCase();
|
|
176
|
+
if (!SCANNABLE_EXTENSIONS.has(ext)) {
|
|
177
|
+
// Special cases
|
|
178
|
+
if (filename.endsWith("Dockerfile"))
|
|
179
|
+
return true;
|
|
180
|
+
if (filename === "docker-compose.yml" || filename === "docker-compose.yaml")
|
|
181
|
+
return true;
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
// Exclude minified/bundled files
|
|
185
|
+
if (filename.endsWith(".min.js") || filename.endsWith(".min.css"))
|
|
186
|
+
return false;
|
|
187
|
+
if (filename.endsWith(".bundle.js"))
|
|
188
|
+
return false;
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Filter changed files to only scannable ones
|
|
193
|
+
*/
|
|
194
|
+
export function filterScannableFiles(files) {
|
|
195
|
+
return files.filter((file) => {
|
|
196
|
+
// Exclude deleted files
|
|
197
|
+
if (file.status === "removed")
|
|
198
|
+
return false;
|
|
199
|
+
return isScannableFile(file.filename);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get the list of file paths to scan
|
|
204
|
+
*/
|
|
205
|
+
export function getFilesToScan(changedFiles) {
|
|
206
|
+
return filterScannableFiles(changedFiles).map((f) => f.filename);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Configure incremental scanning
|
|
210
|
+
*
|
|
211
|
+
* Determines which files need scanning based on:
|
|
212
|
+
* 1. Git diff against base SHA
|
|
213
|
+
* 2. File content hashes (for cache validation)
|
|
214
|
+
* 3. Security-critical file detection
|
|
215
|
+
*/
|
|
216
|
+
export async function configureIncrementalScan(config) {
|
|
217
|
+
const { projectPath, baseSha, headSha, forceInclude, exclude, alwaysScanSecurityFiles } = config;
|
|
218
|
+
// Get git diff
|
|
219
|
+
const diff = await getGitDiff(projectPath, baseSha, headSha);
|
|
220
|
+
// Filter to scannable files
|
|
221
|
+
const scannableChanged = diff.changedFiles.filter(isScannableFile);
|
|
222
|
+
// Apply exclusions
|
|
223
|
+
let filesToProcess = scannableChanged;
|
|
224
|
+
if (exclude && exclude.length > 0) {
|
|
225
|
+
const excludePatterns = exclude.map((p) => new RegExp(p));
|
|
226
|
+
filesToProcess = filesToProcess.filter((f) => !excludePatterns.some((pattern) => pattern.test(f)));
|
|
227
|
+
}
|
|
228
|
+
// Add force-included files
|
|
229
|
+
if (forceInclude && forceInclude.length > 0) {
|
|
230
|
+
const additional = forceInclude.filter((f) => !filesToProcess.includes(f) && isScannableFile(f));
|
|
231
|
+
filesToProcess = [...filesToProcess, ...additional];
|
|
232
|
+
}
|
|
233
|
+
// Add security-critical files if requested
|
|
234
|
+
if (alwaysScanSecurityFiles) {
|
|
235
|
+
const securityFiles = await findSecurityCriticalFiles(projectPath);
|
|
236
|
+
const additional = securityFiles.filter((f) => !filesToProcess.includes(f));
|
|
237
|
+
filesToProcess = [...filesToProcess, ...additional];
|
|
238
|
+
}
|
|
239
|
+
// Get file info (with hashes)
|
|
240
|
+
const fileInfos = await getFilesInfo(projectPath, filesToProcess);
|
|
241
|
+
// Calculate removed files
|
|
242
|
+
const removedFiles = diff.changedFiles.filter((f) => {
|
|
243
|
+
// Try to stat the file - if it fails, it's removed
|
|
244
|
+
return !fileInfos.some((fi) => fi.path === f);
|
|
245
|
+
});
|
|
246
|
+
// Estimate savings
|
|
247
|
+
const totalProjectFiles = await estimateTotalFiles(projectPath);
|
|
248
|
+
const estimatedSavingsPercent = totalProjectFiles > 0
|
|
249
|
+
? Math.round(((totalProjectFiles - fileInfos.length) / totalProjectFiles) * 100)
|
|
250
|
+
: 0;
|
|
251
|
+
const result = {
|
|
252
|
+
filesToScan: fileInfos,
|
|
253
|
+
unchangedFiles: [], // Will be populated by cache lookup
|
|
254
|
+
removedFiles,
|
|
255
|
+
diff,
|
|
256
|
+
config,
|
|
257
|
+
isIncremental: diff.success && fileInfos.length < totalProjectFiles,
|
|
258
|
+
estimatedSavingsPercent,
|
|
259
|
+
};
|
|
260
|
+
logger.info("diff_mode.incremental_config", {
|
|
261
|
+
filesToScan: fileInfos.length,
|
|
262
|
+
removedFiles: removedFiles.length,
|
|
263
|
+
isIncremental: result.isIncremental,
|
|
264
|
+
estimatedSavingsPercent,
|
|
265
|
+
});
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Find security-critical files that should always be scanned
|
|
270
|
+
*/
|
|
271
|
+
async function findSecurityCriticalFiles(projectPath) {
|
|
272
|
+
const patterns = [
|
|
273
|
+
"**/auth/**/*.{ts,tsx,js,jsx}",
|
|
274
|
+
"**/security/**/*.{ts,tsx,js,jsx}",
|
|
275
|
+
"**/middleware/**/*.{ts,tsx,js,jsx}",
|
|
276
|
+
"**/api/**/*.{ts,tsx,js,jsx}",
|
|
277
|
+
"**/*auth*.{ts,tsx,js,jsx}",
|
|
278
|
+
"**/*login*.{ts,tsx,js,jsx}",
|
|
279
|
+
"**/*session*.{ts,tsx,js,jsx}",
|
|
280
|
+
"**/*token*.{ts,tsx,js,jsx}",
|
|
281
|
+
"**/*password*.{ts,tsx,js,jsx}",
|
|
282
|
+
".env.example",
|
|
283
|
+
"package.json",
|
|
284
|
+
"package-lock.json",
|
|
285
|
+
"yarn.lock",
|
|
286
|
+
"pnpm-lock.yaml",
|
|
287
|
+
];
|
|
288
|
+
const files = [];
|
|
289
|
+
try {
|
|
290
|
+
// Use git ls-files to get tracked files matching patterns
|
|
291
|
+
for (const pattern of patterns) {
|
|
292
|
+
try {
|
|
293
|
+
const { stdout } = await execAsync(`git ls-files "${pattern}" 2>/dev/null || true`, { cwd: projectPath, timeout: 5000 });
|
|
294
|
+
const matches = stdout.trim().split("\n").filter((f) => f.length > 0);
|
|
295
|
+
files.push(...matches);
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
// Ignore pattern matching errors
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// Git ls-files failed, return empty
|
|
304
|
+
}
|
|
305
|
+
// Deduplicate
|
|
306
|
+
return [...new Set(files)];
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Estimate total number of scannable files in project
|
|
310
|
+
*/
|
|
311
|
+
async function estimateTotalFiles(projectPath) {
|
|
312
|
+
try {
|
|
313
|
+
const { stdout } = await execAsync('git ls-files | grep -E "\\.(ts|tsx|js|jsx|py|rb|go|java|rs|sql|json|yaml|yml)$" | wc -l', { cwd: projectPath, timeout: 10000 });
|
|
314
|
+
return parseInt(stdout.trim(), 10) || 0;
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return 0;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// ============================================================================
|
|
321
|
+
// GitHub API Integration (preserved from original)
|
|
322
|
+
// ============================================================================
|
|
35
323
|
/**
|
|
36
324
|
* Get changed files from GitHub API
|
|
37
325
|
*
|
|
@@ -72,7 +360,6 @@ octokit, context) {
|
|
|
72
360
|
}
|
|
73
361
|
else {
|
|
74
362
|
// Push event - compare with previous commit
|
|
75
|
-
// For push events, compare HEAD^ with HEAD
|
|
76
363
|
const base = `${context.sha}^`;
|
|
77
364
|
const head = context.sha;
|
|
78
365
|
try {
|
|
@@ -105,70 +392,51 @@ octokit, context) {
|
|
|
105
392
|
}
|
|
106
393
|
return changedFiles;
|
|
107
394
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Filter changed files to only scannable ones
|
|
110
|
-
*/
|
|
111
|
-
export function filterScannableFiles(files) {
|
|
112
|
-
return files.filter((file) => {
|
|
113
|
-
// Exclude deleted files
|
|
114
|
-
if (file.status === "removed")
|
|
115
|
-
return false;
|
|
116
|
-
// Check extension
|
|
117
|
-
const ext = getExtension(file.filename);
|
|
118
|
-
if (!SCANNABLE_EXTENSIONS.has(ext))
|
|
119
|
-
return false;
|
|
120
|
-
// Exclude common non-source paths
|
|
121
|
-
if (file.filename.includes("node_modules/"))
|
|
122
|
-
return false;
|
|
123
|
-
if (file.filename.includes("dist/"))
|
|
124
|
-
return false;
|
|
125
|
-
if (file.filename.includes("build/"))
|
|
126
|
-
return false;
|
|
127
|
-
if (file.filename.includes("coverage/"))
|
|
128
|
-
return false;
|
|
129
|
-
if (file.filename.includes(".git/"))
|
|
130
|
-
return false;
|
|
131
|
-
if (file.filename.endsWith(".min.js"))
|
|
132
|
-
return false;
|
|
133
|
-
if (file.filename.endsWith(".bundle.js"))
|
|
134
|
-
return false;
|
|
135
|
-
return true;
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Get file extension (including dot)
|
|
140
|
-
*/
|
|
141
|
-
function getExtension(filename) {
|
|
142
|
-
const lastDot = filename.lastIndexOf(".");
|
|
143
|
-
if (lastDot === -1)
|
|
144
|
-
return "";
|
|
145
|
-
return filename.slice(lastDot).toLowerCase();
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Get the list of file paths to scan
|
|
149
|
-
*/
|
|
150
|
-
export function getFilesToScan(changedFiles) {
|
|
151
|
-
return filterScannableFiles(changedFiles).map((f) => f.filename);
|
|
152
|
-
}
|
|
153
395
|
/**
|
|
154
396
|
* Check if any security-relevant files changed
|
|
155
397
|
*/
|
|
156
398
|
export function hasSecurityRelevantChanges(changedFiles) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
399
|
+
const securityPatterns = [
|
|
400
|
+
/package\.json$/,
|
|
401
|
+
/package-lock\.json$/,
|
|
402
|
+
/yarn\.lock$/,
|
|
403
|
+
/pnpm-lock\.yaml$/,
|
|
404
|
+
/\.env/,
|
|
405
|
+
/auth/i,
|
|
406
|
+
/security/i,
|
|
407
|
+
/middleware/i,
|
|
408
|
+
/api\//i,
|
|
409
|
+
/routes?\//i,
|
|
410
|
+
/session/i,
|
|
411
|
+
/token/i,
|
|
412
|
+
/password/i,
|
|
413
|
+
/crypt/i,
|
|
414
|
+
/jwt/i,
|
|
415
|
+
/oauth/i,
|
|
416
|
+
/supabase/i,
|
|
417
|
+
/prisma/i,
|
|
418
|
+
/schema\.prisma$/,
|
|
169
419
|
];
|
|
170
|
-
return changedFiles.some((file) =>
|
|
171
|
-
|
|
420
|
+
return changedFiles.some((file) => securityPatterns.some((pattern) => pattern.test(file.filename)));
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get scan scope recommendation based on changes
|
|
424
|
+
*/
|
|
425
|
+
export function getScanScopeRecommendation(changedFiles) {
|
|
426
|
+
// If no changes detected, suggest full scan
|
|
427
|
+
if (changedFiles.length === 0) {
|
|
428
|
+
return "full";
|
|
429
|
+
}
|
|
430
|
+
// If many files changed (>50), suggest full scan
|
|
431
|
+
if (changedFiles.length > 50) {
|
|
432
|
+
return "full";
|
|
433
|
+
}
|
|
434
|
+
// If security-relevant changes, suggest security-focused scan
|
|
435
|
+
if (hasSecurityRelevantChanges(changedFiles)) {
|
|
436
|
+
return "security-only";
|
|
437
|
+
}
|
|
438
|
+
// Otherwise, incremental scan
|
|
439
|
+
return "incremental";
|
|
172
440
|
}
|
|
173
441
|
/**
|
|
174
442
|
* Generate summary of changed files
|
|
@@ -176,19 +444,25 @@ export function hasSecurityRelevantChanges(changedFiles) {
|
|
|
176
444
|
export function generateChangeSummary(changedFiles) {
|
|
177
445
|
const byStatus = new Map();
|
|
178
446
|
const byExtension = new Map();
|
|
447
|
+
let totalAdditions = 0;
|
|
448
|
+
let totalDeletions = 0;
|
|
179
449
|
for (const file of changedFiles) {
|
|
180
450
|
byStatus.set(file.status, (byStatus.get(file.status) || 0) + 1);
|
|
181
|
-
const ext =
|
|
451
|
+
const ext = extname(file.filename) || "(no extension)";
|
|
182
452
|
byExtension.set(ext, (byExtension.get(ext) || 0) + 1);
|
|
453
|
+
totalAdditions += file.additions;
|
|
454
|
+
totalDeletions += file.deletions;
|
|
183
455
|
}
|
|
184
456
|
const lines = [
|
|
185
457
|
`**Changed Files**: ${changedFiles.length}`,
|
|
458
|
+
`**Lines Changed**: +${totalAdditions} / -${totalDeletions}`,
|
|
186
459
|
"",
|
|
187
460
|
"| Status | Count |",
|
|
188
461
|
"|--------|-------|",
|
|
189
462
|
];
|
|
190
463
|
for (const [status, count] of byStatus) {
|
|
191
|
-
|
|
464
|
+
const emoji = status === "added" ? "🆕" : status === "removed" ? "🗑️" : "📝";
|
|
465
|
+
lines.push(`| ${emoji} ${status} | ${count} |`);
|
|
192
466
|
}
|
|
193
467
|
lines.push("", "| Extension | Count |", "|-----------|-------|");
|
|
194
468
|
// Sort by count descending
|
|
@@ -196,6 +470,51 @@ export function generateChangeSummary(changedFiles) {
|
|
|
196
470
|
for (const [ext, count] of sortedExtensions.slice(0, 10)) {
|
|
197
471
|
lines.push(`| ${ext} | ${count} |`);
|
|
198
472
|
}
|
|
473
|
+
// Add scope recommendation
|
|
474
|
+
const scope = getScanScopeRecommendation(changedFiles);
|
|
475
|
+
lines.push("", `**Recommended Scope**: ${scope}`);
|
|
476
|
+
if (hasSecurityRelevantChanges(changedFiles)) {
|
|
477
|
+
lines.push("", "⚠️ **Security-relevant files detected** - recommend full security scan");
|
|
478
|
+
}
|
|
199
479
|
return lines.join("\n");
|
|
200
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Group files by directory for parallel processing
|
|
483
|
+
*/
|
|
484
|
+
export function groupFilesByDirectory(files) {
|
|
485
|
+
const groups = new Map();
|
|
486
|
+
for (const file of files) {
|
|
487
|
+
const dir = file.path.split("/").slice(0, -1).join("/") || ".";
|
|
488
|
+
const existing = groups.get(dir) || [];
|
|
489
|
+
existing.push(file);
|
|
490
|
+
groups.set(dir, existing);
|
|
491
|
+
}
|
|
492
|
+
return groups;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Prioritize files for scanning (security-critical first)
|
|
496
|
+
*/
|
|
497
|
+
export function prioritizeFiles(files) {
|
|
498
|
+
const securityPatterns = [
|
|
499
|
+
/auth/i,
|
|
500
|
+
/security/i,
|
|
501
|
+
/middleware/i,
|
|
502
|
+
/api\//i,
|
|
503
|
+
/routes?\//i,
|
|
504
|
+
/session/i,
|
|
505
|
+
/token/i,
|
|
506
|
+
/password/i,
|
|
507
|
+
/\.env/,
|
|
508
|
+
/package\.json$/,
|
|
509
|
+
];
|
|
510
|
+
return [...files].sort((a, b) => {
|
|
511
|
+
const aIsSecurity = securityPatterns.some((p) => p.test(a.path));
|
|
512
|
+
const bIsSecurity = securityPatterns.some((p) => p.test(b.path));
|
|
513
|
+
if (aIsSecurity && !bIsSecurity)
|
|
514
|
+
return -1;
|
|
515
|
+
if (!aIsSecurity && bIsSecurity)
|
|
516
|
+
return 1;
|
|
517
|
+
return 0;
|
|
518
|
+
});
|
|
519
|
+
}
|
|
201
520
|
//# sourceMappingURL=diff-mode.js.map
|