@vigolium/piolium 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/agents/access-auditor.md +300 -0
- package/agents/assumption-breaker.md +154 -0
- package/agents/attack-designer.md +116 -0
- package/agents/code-scanner.md +139 -0
- package/agents/concurrency-auditor.md +238 -0
- package/agents/confirm-writer.md +257 -0
- package/agents/context-reviewer.md +274 -0
- package/agents/cross-verifier.md +165 -0
- package/agents/cve-scout.md +381 -0
- package/agents/env-builder.md +282 -0
- package/agents/env-profiler.md +205 -0
- package/agents/evidence-collector.md +140 -0
- package/agents/finding-grader.md +142 -0
- package/agents/finding-writer.md +148 -0
- package/agents/flow-tracer.md +106 -0
- package/agents/goal-backtracer.md +146 -0
- package/agents/history-miner.md +467 -0
- package/agents/independent-verifier.md +118 -0
- package/agents/intent-mapper.md +183 -0
- package/agents/longshot-collector.md +128 -0
- package/agents/longshot-prober.md +126 -0
- package/agents/patch-auditor.md +73 -0
- package/agents/poc-author.md +124 -0
- package/agents/poc-runner.md +194 -0
- package/agents/probe-lead.md +269 -0
- package/agents/red-challenger.md +101 -0
- package/agents/report-composer.md +208 -0
- package/agents/review-adjudicator.md +216 -0
- package/agents/spec-auditor.md +155 -0
- package/agents/taint-tracer.md +265 -0
- package/agents/test-locator.md +209 -0
- package/agents/threat-modeler.md +132 -0
- package/agents/variant-scanner.md +108 -0
- package/agents/variant-spotter.md +110 -0
- package/bin/piolium.mjs +376 -0
- package/extensions/piolium/_vendor/yaml.bundle.d.mts +6 -0
- package/extensions/piolium/_vendor/yaml.bundle.mjs +139 -0
- package/extensions/piolium/agent-runner.ts +322 -0
- package/extensions/piolium/agents.ts +266 -0
- package/extensions/piolium/audit-state.ts +522 -0
- package/extensions/piolium/bundled-resources.ts +97 -0
- package/extensions/piolium/candidate-scan.ts +966 -0
- package/extensions/piolium/command-target.ts +177 -0
- package/extensions/piolium/console-stream.ts +57 -0
- package/extensions/piolium/export-results.ts +380 -0
- package/extensions/piolium/findings.ts +448 -0
- package/extensions/piolium/heartbeat.ts +182 -0
- package/extensions/piolium/help.ts +234 -0
- package/extensions/piolium/index.ts +1865 -0
- package/extensions/piolium/longshot.ts +530 -0
- package/extensions/piolium/matcher-suggestions.ts +196 -0
- package/extensions/piolium/matcher-utils.ts +83 -0
- package/extensions/piolium/modes/balanced.ts +750 -0
- package/extensions/piolium/modes/confirm-bootstrap.ts +186 -0
- package/extensions/piolium/modes/confirm.ts +697 -0
- package/extensions/piolium/modes/deep.ts +917 -0
- package/extensions/piolium/modes/diff.ts +177 -0
- package/extensions/piolium/modes/lite.ts +540 -0
- package/extensions/piolium/modes/longshot.ts +595 -0
- package/extensions/piolium/modes/merge.ts +204 -0
- package/extensions/piolium/modes/phase-runner.ts +267 -0
- package/extensions/piolium/modes/reinvest.ts +546 -0
- package/extensions/piolium/modes/revisit.ts +279 -0
- package/extensions/piolium/modes.ts +48 -0
- package/extensions/piolium/phase-labels.ts +123 -0
- package/extensions/piolium/phase-status-strip.ts +92 -0
- package/extensions/piolium/prompt-prefix-editor.ts +39 -0
- package/extensions/piolium/providers/anthropic-vertex.ts +836 -0
- package/extensions/piolium/recon.ts +409 -0
- package/extensions/piolium/result-stats.ts +105 -0
- package/extensions/piolium/retry.ts +120 -0
- package/extensions/piolium/scheduler.ts +212 -0
- package/extensions/piolium/secrets.ts +368 -0
- package/extensions/piolium/tools/web-tools.ts +148 -0
- package/package.json +77 -0
- package/skills/agentic-actions-auditor/SKILL.md +327 -0
- package/skills/agentic-actions-auditor/references/action-profiles.md +186 -0
- package/skills/agentic-actions-auditor/references/cross-file-resolution.md +209 -0
- package/skills/agentic-actions-auditor/references/foundations.md +94 -0
- package/skills/agentic-actions-auditor/references/vector-a-env-var-intermediary.md +77 -0
- package/skills/agentic-actions-auditor/references/vector-b-direct-expression-injection.md +83 -0
- package/skills/agentic-actions-auditor/references/vector-c-cli-data-fetch.md +83 -0
- package/skills/agentic-actions-auditor/references/vector-d-pr-target-checkout.md +88 -0
- package/skills/agentic-actions-auditor/references/vector-e-error-log-injection.md +88 -0
- package/skills/agentic-actions-auditor/references/vector-f-subshell-expansion.md +82 -0
- package/skills/agentic-actions-auditor/references/vector-g-eval-of-ai-output.md +91 -0
- package/skills/agentic-actions-auditor/references/vector-h-dangerous-sandbox-configs.md +102 -0
- package/skills/agentic-actions-auditor/references/vector-i-wildcard-allowlists.md +88 -0
- package/skills/audit/SKILL.md +562 -0
- package/skills/audit/assets/icon.svg +7 -0
- package/skills/audit/hooks/scripts/validate_phase_output.py +550 -0
- package/skills/audit/references/adversarial-review.md +148 -0
- package/skills/audit/references/architecture-aware-sast.md +306 -0
- package/skills/audit/references/audit-workflow.md +737 -0
- package/skills/audit/references/chamber-protocol.md +384 -0
- package/skills/audit/references/creative-attack-modes.md +221 -0
- package/skills/audit/references/deep-analysis.md +273 -0
- package/skills/audit/references/domain-attack-playbooks.md +1129 -0
- package/skills/audit/references/knowledge-base-template.md +513 -0
- package/skills/audit/references/real-env-validation.md +191 -0
- package/skills/audit/references/report-templates.md +417 -0
- package/skills/audit/references/triage-and-prereqs.md +134 -0
- package/skills/audit/scripts/consolidate_drafts.py +554 -0
- package/skills/audit/scripts/partition_findings.py +152 -0
- package/skills/audit/scripts/rg-hotspots.sh +121 -0
- package/skills/audit/scripts/stamp_file_state.py +349 -0
- package/skills/code-reviewer/SKILL.md +65 -0
- package/skills/codeql/SKILL.md +281 -0
- package/skills/codeql/references/build-fixes.md +90 -0
- package/skills/codeql/references/diagnostic-query-templates.md +339 -0
- package/skills/codeql/references/extension-yaml-format.md +209 -0
- package/skills/codeql/references/important-only-suite.md +153 -0
- package/skills/codeql/references/language-details.md +207 -0
- package/skills/codeql/references/macos-arm64e-workaround.md +179 -0
- package/skills/codeql/references/performance-tuning.md +111 -0
- package/skills/codeql/references/quality-assessment.md +172 -0
- package/skills/codeql/references/ruleset-catalog.md +63 -0
- package/skills/codeql/references/run-all-suite.md +92 -0
- package/skills/codeql/references/sarif-processing.md +79 -0
- package/skills/codeql/references/threat-models.md +51 -0
- package/skills/codeql/workflows/build-database.md +280 -0
- package/skills/codeql/workflows/create-data-extensions.md +261 -0
- package/skills/codeql/workflows/run-analysis.md +301 -0
- package/skills/differential-review/SKILL.md +220 -0
- package/skills/differential-review/adversarial.md +203 -0
- package/skills/differential-review/methodology.md +234 -0
- package/skills/differential-review/patterns.md +300 -0
- package/skills/differential-review/reporting.md +369 -0
- package/skills/fp-check/SKILL.md +125 -0
- package/skills/fp-check/references/bug-class-verification.md +114 -0
- package/skills/fp-check/references/deep-verification.md +143 -0
- package/skills/fp-check/references/evidence-templates.md +91 -0
- package/skills/fp-check/references/false-positive-patterns.md +115 -0
- package/skills/fp-check/references/gate-reviews.md +27 -0
- package/skills/fp-check/references/standard-verification.md +78 -0
- package/skills/insecure-defaults/SKILL.md +117 -0
- package/skills/insecure-defaults/references/examples.md +409 -0
- package/skills/last30days/SKILL.md +444 -0
- package/skills/sarif-parsing/SKILL.md +483 -0
- package/skills/sarif-parsing/resources/jq-queries.md +162 -0
- package/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
- package/skills/security-threat-model/LICENSE.txt +201 -0
- package/skills/security-threat-model/SKILL.md +81 -0
- package/skills/security-threat-model/agents/openai.yaml +4 -0
- package/skills/security-threat-model/references/prompt-template.md +255 -0
- package/skills/security-threat-model/references/security-controls-and-assets.md +32 -0
- package/skills/semgrep/SKILL.md +212 -0
- package/skills/semgrep/references/rulesets.md +162 -0
- package/skills/semgrep/references/scan-modes.md +110 -0
- package/skills/semgrep/references/scanner-task-prompt.md +140 -0
- package/skills/semgrep/scripts/merge_sarif.py +203 -0
- package/skills/semgrep/workflows/scan-workflow.md +311 -0
- package/skills/semgrep-rule-creator/SKILL.md +168 -0
- package/skills/semgrep-rule-creator/references/quick-reference.md +202 -0
- package/skills/semgrep-rule-creator/references/workflow.md +240 -0
- package/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
- package/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
- package/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
- package/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
- package/skills/sharp-edges/SKILL.md +292 -0
- package/skills/sharp-edges/references/auth-patterns.md +252 -0
- package/skills/sharp-edges/references/case-studies.md +274 -0
- package/skills/sharp-edges/references/config-patterns.md +333 -0
- package/skills/sharp-edges/references/crypto-apis.md +190 -0
- package/skills/sharp-edges/references/lang-c.md +205 -0
- package/skills/sharp-edges/references/lang-csharp.md +285 -0
- package/skills/sharp-edges/references/lang-go.md +270 -0
- package/skills/sharp-edges/references/lang-java.md +263 -0
- package/skills/sharp-edges/references/lang-javascript.md +269 -0
- package/skills/sharp-edges/references/lang-kotlin.md +265 -0
- package/skills/sharp-edges/references/lang-php.md +245 -0
- package/skills/sharp-edges/references/lang-python.md +274 -0
- package/skills/sharp-edges/references/lang-ruby.md +273 -0
- package/skills/sharp-edges/references/lang-rust.md +272 -0
- package/skills/sharp-edges/references/lang-swift.md +287 -0
- package/skills/sharp-edges/references/language-specific.md +588 -0
- package/skills/spec-to-code-compliance/SKILL.md +357 -0
- package/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
- package/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
- package/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
- package/skills/supply-chain-risk-auditor/SKILL.md +67 -0
- package/skills/supply-chain-risk-auditor/resources/results-template.md +41 -0
- package/skills/variant-analysis/METHODOLOGY.md +327 -0
- package/skills/variant-analysis/SKILL.md +142 -0
- package/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
- package/skills/variant-analysis/resources/codeql/go.ql +69 -0
- package/skills/variant-analysis/resources/codeql/java.ql +71 -0
- package/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
- package/skills/variant-analysis/resources/codeql/python.ql +80 -0
- package/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
- package/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
- package/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
- package/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
- package/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
- package/skills/variant-analysis/resources/variant-report-template.md +75 -0
- package/skills/vuln-report/SKILL.md +137 -0
- package/skills/vuln-report/agents/openai.yaml +4 -0
- package/skills/vuln-report/references/report-template.md +135 -0
- package/skills/wooyun-legacy/SKILL.md +367 -0
- package/skills/wooyun-legacy/references/bank-penetration.md +222 -0
- package/skills/wooyun-legacy/references/checklists/command-execution-checklist.md +119 -0
- package/skills/wooyun-legacy/references/checklists/csrf-checklist.md +74 -0
- package/skills/wooyun-legacy/references/checklists/file-upload-checklist.md +108 -0
- package/skills/wooyun-legacy/references/checklists/info-disclosure-checklist.md +114 -0
- package/skills/wooyun-legacy/references/checklists/logic-flaws-checklist.md +95 -0
- package/skills/wooyun-legacy/references/checklists/misconfig-checklist.md +124 -0
- package/skills/wooyun-legacy/references/checklists/path-traversal-checklist.md +87 -0
- package/skills/wooyun-legacy/references/checklists/rce-checklist.md +93 -0
- package/skills/wooyun-legacy/references/checklists/sql-injection-checklist.md +97 -0
- package/skills/wooyun-legacy/references/checklists/ssrf-checklist.md +99 -0
- package/skills/wooyun-legacy/references/checklists/unauthorized-access-checklist.md +89 -0
- package/skills/wooyun-legacy/references/checklists/weak-password-checklist.md +115 -0
- package/skills/wooyun-legacy/references/checklists/xss-checklist.md +103 -0
- package/skills/wooyun-legacy/references/checklists/xxe-checklist.md +130 -0
- package/skills/wooyun-legacy/references/info-disclosure.md +975 -0
- package/skills/wooyun-legacy/references/logic-flaws.md +721 -0
- package/skills/wooyun-legacy/references/path-traversal.md +1191 -0
- package/skills/wooyun-legacy/references/telecom-penetration.md +156 -0
- package/skills/wooyun-legacy/references/unauthorized-access.md +980 -0
- package/skills/wooyun-legacy/references/xss.md +746 -0
- package/skills/zeroize-audit/SKILL.md +371 -0
- package/skills/zeroize-audit/configs/c.yaml +21 -0
- package/skills/zeroize-audit/configs/default.yaml +128 -0
- package/skills/zeroize-audit/configs/rust.yaml +83 -0
- package/skills/zeroize-audit/prompts/report_template.md +238 -0
- package/skills/zeroize-audit/prompts/system.md +163 -0
- package/skills/zeroize-audit/prompts/task.md +97 -0
- package/skills/zeroize-audit/references/compile-commands.md +231 -0
- package/skills/zeroize-audit/references/detection-strategy.md +191 -0
- package/skills/zeroize-audit/references/ir-analysis.md +252 -0
- package/skills/zeroize-audit/references/mcp-analysis.md +221 -0
- package/skills/zeroize-audit/references/poc-generation.md +470 -0
- package/skills/zeroize-audit/references/rust-zeroization-patterns.md +867 -0
- package/skills/zeroize-audit/schemas/input.json +83 -0
- package/skills/zeroize-audit/schemas/output.json +140 -0
- package/skills/zeroize-audit/tools/analyze_asm.sh +202 -0
- package/skills/zeroize-audit/tools/analyze_cfg.py +381 -0
- package/skills/zeroize-audit/tools/analyze_heap.sh +211 -0
- package/skills/zeroize-audit/tools/analyze_ir_semantic.py +429 -0
- package/skills/zeroize-audit/tools/diff_ir.sh +135 -0
- package/skills/zeroize-audit/tools/diff_rust_mir.sh +189 -0
- package/skills/zeroize-audit/tools/emit_asm.sh +67 -0
- package/skills/zeroize-audit/tools/emit_ir.sh +77 -0
- package/skills/zeroize-audit/tools/emit_rust_asm.sh +178 -0
- package/skills/zeroize-audit/tools/emit_rust_ir.sh +150 -0
- package/skills/zeroize-audit/tools/emit_rust_mir.sh +158 -0
- package/skills/zeroize-audit/tools/extract_compile_flags.py +284 -0
- package/skills/zeroize-audit/tools/generate_poc.py +1329 -0
- package/skills/zeroize-audit/tools/mcp/apply_confidence_gates.py +113 -0
- package/skills/zeroize-audit/tools/mcp/check_mcp.sh +68 -0
- package/skills/zeroize-audit/tools/mcp/normalize_mcp_evidence.py +125 -0
- package/skills/zeroize-audit/tools/scripts/check_llvm_patterns.py +481 -0
- package/skills/zeroize-audit/tools/scripts/check_mir_patterns.py +554 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm.py +424 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm_aarch64.py +300 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm_x86.py +283 -0
- package/skills/zeroize-audit/tools/scripts/find_dangerous_apis.py +375 -0
- package/skills/zeroize-audit/tools/scripts/semantic_audit.py +923 -0
- package/skills/zeroize-audit/tools/track_dataflow.sh +196 -0
- package/skills/zeroize-audit/tools/validate_rust_toolchain.sh +298 -0
- package/skills/zeroize-audit/workflows/phase-0-preflight.md +150 -0
- package/skills/zeroize-audit/workflows/phase-1-source-analysis.md +144 -0
- package/skills/zeroize-audit/workflows/phase-2-compiler-analysis.md +139 -0
- package/skills/zeroize-audit/workflows/phase-3-interim-report.md +46 -0
- package/skills/zeroize-audit/workflows/phase-4-poc-generation.md +46 -0
- package/skills/zeroize-audit/workflows/phase-5-poc-validation.md +136 -0
- package/skills/zeroize-audit/workflows/phase-6-final-report.md +44 -0
- package/skills/zeroize-audit/workflows/phase-7-test-generation.md +42 -0
- package/themes/piolium-srcery.json +94 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Longshot mode — file enumeration, scoring, and per-file status sidecar.
|
|
3
|
+
*
|
|
4
|
+
* Pure deterministic helpers used by `modes/longshot.ts`. No model calls.
|
|
5
|
+
*
|
|
6
|
+
* The hail-mary scan picks files matching the dominant project language(s),
|
|
7
|
+
* filters out tests and generated code, scores each file by the density of
|
|
8
|
+
* dangerous-looking tokens + path heuristics, then writes the ordered list
|
|
9
|
+
* to `piolium/attack-surface/longshot-targets.json`. The X2 fan-out reads
|
|
10
|
+
* that sidecar and updates per-file status atomically as it makes progress,
|
|
11
|
+
* which keeps resume cheap (skip files already complete; retry the rest).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import {
|
|
16
|
+
existsSync,
|
|
17
|
+
mkdirSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
readdirSync,
|
|
20
|
+
renameSync,
|
|
21
|
+
statSync,
|
|
22
|
+
writeFileSync,
|
|
23
|
+
} from "node:fs";
|
|
24
|
+
import { dirname, join, relative } from "node:path";
|
|
25
|
+
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
26
|
+
import { readCandidateScores } from "./candidate-scan.ts";
|
|
27
|
+
|
|
28
|
+
export interface LongshotTarget {
|
|
29
|
+
path: string;
|
|
30
|
+
language: string;
|
|
31
|
+
score: number;
|
|
32
|
+
bytes: number;
|
|
33
|
+
sha8: string;
|
|
34
|
+
status: "pending" | "in_progress" | "complete" | "failed";
|
|
35
|
+
attempts?: number;
|
|
36
|
+
last_error?: string;
|
|
37
|
+
completed_at?: string;
|
|
38
|
+
run_id?: string;
|
|
39
|
+
draft_count?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface LongshotTargetsFile {
|
|
43
|
+
generated_at: string;
|
|
44
|
+
cwd: string;
|
|
45
|
+
languages: string[];
|
|
46
|
+
limit: number;
|
|
47
|
+
total_candidates: number;
|
|
48
|
+
skipped_tests: number;
|
|
49
|
+
skipped_generated: number;
|
|
50
|
+
skipped_oversized: number;
|
|
51
|
+
skipped_unrecognized: number;
|
|
52
|
+
targets: LongshotTarget[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const LONGSHOT_ATTACK_SURFACE_DIR = "piolium/attack-surface";
|
|
56
|
+
export const LONGSHOT_TARGETS_PATH = `${LONGSHOT_ATTACK_SURFACE_DIR}/longshot-targets.json`;
|
|
57
|
+
export const LONGSHOT_SUMMARY_PATH = `${LONGSHOT_ATTACK_SURFACE_DIR}/longshot-summary.md`;
|
|
58
|
+
export const LONGSHOT_FINDINGS_DRAFT_DIR = "piolium/findings-draft";
|
|
59
|
+
export const LONGSHOT_DEFAULT_LIMIT = 1000;
|
|
60
|
+
export const LONGSHOT_DEFAULT_TIMEOUT_MS = 6 * 60 * 60 * 1000; // 6 hours
|
|
61
|
+
export const LONGSHOT_MAX_FILE_BYTES = 1024 * 1024; // skip files > 1MB
|
|
62
|
+
|
|
63
|
+
/** Snake-case-key extension to languages most users will hunt against. */
|
|
64
|
+
const LANGUAGE_BY_EXT: Record<string, string> = {
|
|
65
|
+
".ts": "TypeScript",
|
|
66
|
+
".tsx": "TypeScript",
|
|
67
|
+
".js": "JavaScript",
|
|
68
|
+
".jsx": "JavaScript",
|
|
69
|
+
".mjs": "JavaScript",
|
|
70
|
+
".cjs": "JavaScript",
|
|
71
|
+
".py": "Python",
|
|
72
|
+
".go": "Go",
|
|
73
|
+
".rs": "Rust",
|
|
74
|
+
".rb": "Ruby",
|
|
75
|
+
".java": "Java",
|
|
76
|
+
".kt": "Kotlin",
|
|
77
|
+
".swift": "Swift",
|
|
78
|
+
".c": "C",
|
|
79
|
+
".h": "C",
|
|
80
|
+
".cpp": "C++",
|
|
81
|
+
".cc": "C++",
|
|
82
|
+
".hpp": "C++",
|
|
83
|
+
".cs": "C#",
|
|
84
|
+
".php": "PHP",
|
|
85
|
+
".scala": "Scala",
|
|
86
|
+
".clj": "Clojure",
|
|
87
|
+
".sh": "Shell",
|
|
88
|
+
".bash": "Shell",
|
|
89
|
+
".zsh": "Shell",
|
|
90
|
+
".lua": "Lua",
|
|
91
|
+
".m": "Objective-C",
|
|
92
|
+
".mm": "Objective-C",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const EXTS_BY_LANGUAGE: Record<string, string[]> = (() => {
|
|
96
|
+
const map: Record<string, string[]> = {};
|
|
97
|
+
for (const [ext, lang] of Object.entries(LANGUAGE_BY_EXT)) {
|
|
98
|
+
let list = map[lang];
|
|
99
|
+
if (!list) {
|
|
100
|
+
list = [];
|
|
101
|
+
map[lang] = list;
|
|
102
|
+
}
|
|
103
|
+
list.push(ext);
|
|
104
|
+
}
|
|
105
|
+
return map;
|
|
106
|
+
})();
|
|
107
|
+
|
|
108
|
+
const SKIP_DIRS = new Set([
|
|
109
|
+
"node_modules",
|
|
110
|
+
".git",
|
|
111
|
+
"vendor",
|
|
112
|
+
"dist",
|
|
113
|
+
"build",
|
|
114
|
+
"target",
|
|
115
|
+
"out",
|
|
116
|
+
".next",
|
|
117
|
+
".nuxt",
|
|
118
|
+
".cache",
|
|
119
|
+
".venv",
|
|
120
|
+
"venv",
|
|
121
|
+
"__pycache__",
|
|
122
|
+
".pytest_cache",
|
|
123
|
+
".mypy_cache",
|
|
124
|
+
".idea",
|
|
125
|
+
".vscode",
|
|
126
|
+
"piolium",
|
|
127
|
+
"third_party",
|
|
128
|
+
"third-party",
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
const TEST_DIR_NAMES = new Set([
|
|
132
|
+
"tests",
|
|
133
|
+
"test",
|
|
134
|
+
"__tests__",
|
|
135
|
+
"spec",
|
|
136
|
+
"specs",
|
|
137
|
+
"e2e",
|
|
138
|
+
"fixtures",
|
|
139
|
+
"testdata",
|
|
140
|
+
"test-data",
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
const TEST_FILE_PATTERNS: RegExp[] = [
|
|
144
|
+
/(^|\/)test_[^/]+\.py$/i,
|
|
145
|
+
/(^|\/)[^/]+_test\.py$/i,
|
|
146
|
+
/(^|\/)[^/]+_test\.go$/i,
|
|
147
|
+
/(^|\/)[^/]+\.test\.(?:ts|tsx|js|jsx|mjs|cjs)$/i,
|
|
148
|
+
/(^|\/)[^/]+\.spec\.(?:ts|tsx|js|jsx|mjs|cjs)$/i,
|
|
149
|
+
/(^|\/)[^/]+\.test\.rb$/i,
|
|
150
|
+
/(^|\/)[^/]+_spec\.rb$/i,
|
|
151
|
+
/(^|\/)[^/]+(?:Test|Tests|Spec|Specs)\.(?:java|kt|kts|scala|cs)$/,
|
|
152
|
+
/(^|\/)test_[^/]+\.rs$/i,
|
|
153
|
+
/(^|\/)[^/]+\.test\.[^/]*$/i,
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const GENERATED_FILE_PATTERNS: RegExp[] = [
|
|
157
|
+
/\.pb\.go$/i,
|
|
158
|
+
/_pb2\.py$/i,
|
|
159
|
+
/_pb2_grpc\.py$/i,
|
|
160
|
+
/\.pb\.cc$/i,
|
|
161
|
+
/\.pb\.h$/i,
|
|
162
|
+
/\.gen\.go$/i,
|
|
163
|
+
/_generated\./i,
|
|
164
|
+
/\.generated\./i,
|
|
165
|
+
/\.min\.js$/i,
|
|
166
|
+
/\.min\.css$/i,
|
|
167
|
+
/\.bundle\.js$/i,
|
|
168
|
+
/-generated\./i,
|
|
169
|
+
/^bindata\.go$/i,
|
|
170
|
+
/_string\.go$/i,
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
/** Substrings that strongly hint a file is interesting to a vulnerability hunter. */
|
|
174
|
+
const CONTENT_SIGNALS: Array<{ pattern: RegExp; weight: number }> = [
|
|
175
|
+
{ pattern: /os\/exec|exec\.Command|subprocess|child_process|popen\(|Runtime\.exec/g, weight: 6 },
|
|
176
|
+
{ pattern: /\beval\(|new Function\(|Function\(['"]/g, weight: 7 },
|
|
177
|
+
{ pattern: /unsafe[A-Za-z_]*|reflect\.unsafe/g, weight: 4 },
|
|
178
|
+
{ pattern: /db\.(?:Exec|Query|Raw)|raw_query|cursor\.execute|conn\.query/gi, weight: 5 },
|
|
179
|
+
{ pattern: /\bSELECT\b[\s\S]{0,80}\bFROM\b/gi, weight: 3 },
|
|
180
|
+
{
|
|
181
|
+
pattern: /http\.(?:Handle|HandleFunc|Get|Post|Put|Delete)|router\.|app\.(?:get|post|put|delete)/g,
|
|
182
|
+
weight: 5,
|
|
183
|
+
},
|
|
184
|
+
{ pattern: /jwt|oauth|saml|sso|auth(?:enticate|orize)?/gi, weight: 4 },
|
|
185
|
+
{
|
|
186
|
+
pattern:
|
|
187
|
+
/\b(headers\s*\(\s*\)|request\.headers|req\.headers|getHeader|Header\.Get|x-forwarded-|x-real-ip|x-original-|x-rewrite-url|x-http-method-override|x-(?:user|auth|tenant|org|admin|internal|debug|preview|middleware))/gi,
|
|
188
|
+
weight: 5,
|
|
189
|
+
},
|
|
190
|
+
{ pattern: /password|passwd|secret|api[_-]?key|token|credential/gi, weight: 3 },
|
|
191
|
+
{ pattern: /crypto|md5|sha1|aes|rsa|pkcs|encrypt|decrypt|sign\(|verify\(/gi, weight: 3 },
|
|
192
|
+
{ pattern: /pickle\.loads|yaml\.load|fromXml|XMLDecoder|unmarshal|deserialize/gi, weight: 6 },
|
|
193
|
+
{ pattern: /readFile|writeFile|open\(|fopen|os\.path\.join|filepath\.Join/gi, weight: 2 },
|
|
194
|
+
{ pattern: /redirect|sendFile|res\.send|response\.write|res\.json/gi, weight: 2 },
|
|
195
|
+
{ pattern: /\.\.\/|path\.resolve|path\.join|os\.path\.abspath/g, weight: 2 },
|
|
196
|
+
{ pattern: /shell=True|cmd \/c|sh -c|bash -c/g, weight: 6 },
|
|
197
|
+
{ pattern: /requests\.(?:get|post)|axios|fetch\(|http\.Get|net\/http/g, weight: 3 },
|
|
198
|
+
{ pattern: /\bSSRF\b|\bRCE\b|\bXXE\b|\bSSTI\b|\bIDOR\b|\bCSRF\b|\bXSS\b/g, weight: 5 },
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
/** Path-segment hints; weights stack additively. */
|
|
202
|
+
const PATH_SIGNALS: Array<{ segment: string; weight: number }> = [
|
|
203
|
+
{ segment: "cmd", weight: 4 },
|
|
204
|
+
{ segment: "main", weight: 3 },
|
|
205
|
+
{ segment: "handlers", weight: 5 },
|
|
206
|
+
{ segment: "handler", weight: 5 },
|
|
207
|
+
{ segment: "routes", weight: 5 },
|
|
208
|
+
{ segment: "route", weight: 5 },
|
|
209
|
+
{ segment: "controllers", weight: 5 },
|
|
210
|
+
{ segment: "controller", weight: 5 },
|
|
211
|
+
{ segment: "api", weight: 4 },
|
|
212
|
+
{ segment: "auth", weight: 5 },
|
|
213
|
+
{ segment: "middleware", weight: 4 },
|
|
214
|
+
{ segment: "server", weight: 3 },
|
|
215
|
+
{ segment: "rpc", weight: 4 },
|
|
216
|
+
{ segment: "gateway", weight: 4 },
|
|
217
|
+
{ segment: "session", weight: 3 },
|
|
218
|
+
{ segment: "permissions", weight: 4 },
|
|
219
|
+
{ segment: "users", weight: 3 },
|
|
220
|
+
{ segment: "admin", weight: 4 },
|
|
221
|
+
{ segment: "upload", weight: 5 },
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
export interface EnumerateOptions {
|
|
225
|
+
cwd: string;
|
|
226
|
+
languages?: string[];
|
|
227
|
+
limit?: number;
|
|
228
|
+
includeTests?: boolean;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface EnumerateResult extends LongshotTargetsFile {}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Walk the repo, score every candidate, and return the top `limit` ordered by
|
|
235
|
+
* descending interestingness. Files that are too large, test, or generated
|
|
236
|
+
* are filtered out before scoring.
|
|
237
|
+
*
|
|
238
|
+
* Caller is expected to write the result via `writeLongshotTargets` so the
|
|
239
|
+
* file lands at the canonical path.
|
|
240
|
+
*/
|
|
241
|
+
export function enumerateTargets(options: EnumerateOptions): EnumerateResult {
|
|
242
|
+
const cwd = options.cwd;
|
|
243
|
+
const includeTests = options.includeTests ?? false;
|
|
244
|
+
const limit = options.limit ?? LONGSHOT_DEFAULT_LIMIT;
|
|
245
|
+
|
|
246
|
+
const requestedLanguages = options.languages?.length ? options.languages : detectLanguages(cwd);
|
|
247
|
+
const allowedExts = expandExtensions(requestedLanguages);
|
|
248
|
+
|
|
249
|
+
let skippedTests = 0;
|
|
250
|
+
let skippedGenerated = 0;
|
|
251
|
+
let skippedOversized = 0;
|
|
252
|
+
let skippedUnrecognized = 0;
|
|
253
|
+
|
|
254
|
+
const candidates: LongshotTarget[] = [];
|
|
255
|
+
const candidateScores = readCandidateScores(cwd);
|
|
256
|
+
|
|
257
|
+
const walk = (dir: string): void => {
|
|
258
|
+
let entries: string[];
|
|
259
|
+
try {
|
|
260
|
+
entries = readdirSync(dir);
|
|
261
|
+
} catch {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
for (const entry of entries) {
|
|
265
|
+
if (SKIP_DIRS.has(entry)) continue;
|
|
266
|
+
if (entry.startsWith(".")) continue;
|
|
267
|
+
const full = join(dir, entry);
|
|
268
|
+
let st: ReturnType<typeof statSync>;
|
|
269
|
+
try {
|
|
270
|
+
st = statSync(full);
|
|
271
|
+
} catch {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (st.isDirectory()) {
|
|
275
|
+
if (!includeTests && TEST_DIR_NAMES.has(entry.toLowerCase())) {
|
|
276
|
+
skippedTests++;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
walk(full);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (!st.isFile()) continue;
|
|
283
|
+
|
|
284
|
+
const rel = relative(cwd, full).split("\\").join("/");
|
|
285
|
+
const ext = extOf(entry);
|
|
286
|
+
if (!ext || !allowedExts.has(ext)) {
|
|
287
|
+
skippedUnrecognized++;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (!includeTests && isTestFile(rel)) {
|
|
291
|
+
skippedTests++;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (isGeneratedFile(rel)) {
|
|
295
|
+
skippedGenerated++;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (st.size > LONGSHOT_MAX_FILE_BYTES) {
|
|
299
|
+
skippedOversized++;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let content = "";
|
|
304
|
+
try {
|
|
305
|
+
content = readFileSync(full, "utf8");
|
|
306
|
+
} catch {
|
|
307
|
+
// Binary or unreadable; treat as zero score but keep ext-detected files.
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const language = LANGUAGE_BY_EXT[ext] ?? "Unknown";
|
|
311
|
+
const score = scoreFile(rel, content) + Math.min(candidateScores.get(rel) ?? 0, 250);
|
|
312
|
+
const sha8 = createHash("sha1").update(rel).digest("hex").slice(0, 8);
|
|
313
|
+
|
|
314
|
+
candidates.push({
|
|
315
|
+
path: rel,
|
|
316
|
+
language,
|
|
317
|
+
score,
|
|
318
|
+
bytes: st.size,
|
|
319
|
+
sha8,
|
|
320
|
+
status: "pending",
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
walk(cwd);
|
|
326
|
+
|
|
327
|
+
candidates.sort((a, b) => {
|
|
328
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
329
|
+
// Stable secondary sort: shorter paths first, then lexicographic.
|
|
330
|
+
if (a.path.length !== b.path.length) return a.path.length - b.path.length;
|
|
331
|
+
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const targets = candidates.slice(0, Math.max(0, limit));
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
generated_at: new Date().toISOString(),
|
|
338
|
+
cwd,
|
|
339
|
+
languages: requestedLanguages,
|
|
340
|
+
limit,
|
|
341
|
+
total_candidates: candidates.length,
|
|
342
|
+
skipped_tests: skippedTests,
|
|
343
|
+
skipped_generated: skippedGenerated,
|
|
344
|
+
skipped_oversized: skippedOversized,
|
|
345
|
+
skipped_unrecognized: skippedUnrecognized,
|
|
346
|
+
targets,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function scoreFile(relPath: string, content: string): number {
|
|
351
|
+
let score = 0;
|
|
352
|
+
const lower = relPath.toLowerCase();
|
|
353
|
+
for (const { segment, weight } of PATH_SIGNALS) {
|
|
354
|
+
if (lower.includes(`/${segment}/`) || lower.startsWith(`${segment}/`)) score += weight;
|
|
355
|
+
}
|
|
356
|
+
if (!content) return score;
|
|
357
|
+
// Cap the haystack to avoid quadratic regex blowups on very large files.
|
|
358
|
+
const haystack = content.length > 256 * 1024 ? content.slice(0, 256 * 1024) : content;
|
|
359
|
+
for (const { pattern, weight } of CONTENT_SIGNALS) {
|
|
360
|
+
const matches = haystack.match(pattern);
|
|
361
|
+
if (matches) score += weight * Math.min(matches.length, 10);
|
|
362
|
+
}
|
|
363
|
+
return score;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function detectLanguages(cwd: string): string[] {
|
|
367
|
+
const counts: Record<string, number> = {};
|
|
368
|
+
const walk = (dir: string, depth: number): void => {
|
|
369
|
+
if (depth > 6) return;
|
|
370
|
+
let entries: string[];
|
|
371
|
+
try {
|
|
372
|
+
entries = readdirSync(dir);
|
|
373
|
+
} catch {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
for (const entry of entries) {
|
|
377
|
+
if (SKIP_DIRS.has(entry)) continue;
|
|
378
|
+
if (entry.startsWith(".")) continue;
|
|
379
|
+
const full = join(dir, entry);
|
|
380
|
+
let st: ReturnType<typeof statSync>;
|
|
381
|
+
try {
|
|
382
|
+
st = statSync(full);
|
|
383
|
+
} catch {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (st.isDirectory()) {
|
|
387
|
+
walk(full, depth + 1);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
const ext = extOf(entry);
|
|
391
|
+
if (!ext) continue;
|
|
392
|
+
const lang = LANGUAGE_BY_EXT[ext];
|
|
393
|
+
if (!lang) continue;
|
|
394
|
+
counts[lang] = (counts[lang] ?? 0) + 1;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
walk(cwd, 0);
|
|
398
|
+
const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
|
|
399
|
+
if (sorted.length === 0) return [];
|
|
400
|
+
const top = sorted[0]?.[1] ?? 0;
|
|
401
|
+
// Include any language with at least 25% of the top language's file count
|
|
402
|
+
// so polyglot repos (e.g. Go + Python) get full coverage.
|
|
403
|
+
return sorted.filter(([, count]) => count >= top * 0.25).map(([lang]) => lang);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export function expandExtensions(languages: string[]): Set<string> {
|
|
407
|
+
const out = new Set<string>();
|
|
408
|
+
for (const lang of languages) {
|
|
409
|
+
const exts = EXTS_BY_LANGUAGE[lang];
|
|
410
|
+
if (exts) for (const ext of exts) out.add(ext);
|
|
411
|
+
}
|
|
412
|
+
return out;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function isTestFile(relPath: string): boolean {
|
|
416
|
+
for (const re of TEST_FILE_PATTERNS) {
|
|
417
|
+
if (re.test(relPath)) return true;
|
|
418
|
+
}
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export function isGeneratedFile(relPath: string): boolean {
|
|
423
|
+
for (const re of GENERATED_FILE_PATTERNS) {
|
|
424
|
+
if (re.test(relPath)) return true;
|
|
425
|
+
}
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function extOf(filename: string): string | undefined {
|
|
430
|
+
const dot = filename.lastIndexOf(".");
|
|
431
|
+
if (dot <= 0) return undefined;
|
|
432
|
+
return filename.slice(dot).toLowerCase();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function longshotTargetsPath(cwd: string): string {
|
|
436
|
+
return join(cwd, LONGSHOT_TARGETS_PATH);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export function readLongshotTargets(cwd: string): LongshotTargetsFile | undefined {
|
|
440
|
+
const path = longshotTargetsPath(cwd);
|
|
441
|
+
if (!existsSync(path)) return undefined;
|
|
442
|
+
try {
|
|
443
|
+
const raw = readFileSync(path, "utf8");
|
|
444
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
445
|
+
if (
|
|
446
|
+
parsed &&
|
|
447
|
+
typeof parsed === "object" &&
|
|
448
|
+
Array.isArray((parsed as { targets?: unknown }).targets)
|
|
449
|
+
) {
|
|
450
|
+
return parsed as LongshotTargetsFile;
|
|
451
|
+
}
|
|
452
|
+
} catch {
|
|
453
|
+
// fall through
|
|
454
|
+
}
|
|
455
|
+
return undefined;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Atomically rewrite the targets file under the file mutation queue. The
|
|
460
|
+
* transformer receives the current state (or `undefined` if missing) and
|
|
461
|
+
* returns the next state. Returning `undefined` aborts the write.
|
|
462
|
+
*/
|
|
463
|
+
export async function mutateLongshotTargets(
|
|
464
|
+
cwd: string,
|
|
465
|
+
transform: (state: LongshotTargetsFile | undefined) => LongshotTargetsFile | undefined,
|
|
466
|
+
): Promise<LongshotTargetsFile | undefined> {
|
|
467
|
+
const path = longshotTargetsPath(cwd);
|
|
468
|
+
return withFileMutationQueue(path, async () => {
|
|
469
|
+
const current = readLongshotTargets(cwd);
|
|
470
|
+
const next = transform(current);
|
|
471
|
+
if (!next) return current;
|
|
472
|
+
writeAtomic(path, `${JSON.stringify(next, null, "\t")}\n`);
|
|
473
|
+
return next;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function writeAtomic(path: string, content: string): void {
|
|
478
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
479
|
+
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
480
|
+
writeFileSync(tmp, content);
|
|
481
|
+
renameSync(tmp, path);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export function writeLongshotTargets(cwd: string, file: LongshotTargetsFile): void {
|
|
485
|
+
writeAtomic(longshotTargetsPath(cwd), `${JSON.stringify(file, null, "\t")}\n`);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export interface UpdateTargetStatusOptions {
|
|
489
|
+
status: LongshotTarget["status"];
|
|
490
|
+
last_error?: string;
|
|
491
|
+
completed_at?: string;
|
|
492
|
+
run_id?: string;
|
|
493
|
+
draft_count?: number;
|
|
494
|
+
incrementAttempts?: boolean;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export async function updateTargetStatus(
|
|
498
|
+
cwd: string,
|
|
499
|
+
path: string,
|
|
500
|
+
update: UpdateTargetStatusOptions,
|
|
501
|
+
): Promise<void> {
|
|
502
|
+
await mutateLongshotTargets(cwd, (state) => {
|
|
503
|
+
if (!state) return undefined;
|
|
504
|
+
const idx = state.targets.findIndex((t) => t.path === path);
|
|
505
|
+
if (idx < 0) return undefined;
|
|
506
|
+
const prev = state.targets[idx];
|
|
507
|
+
if (!prev) return undefined;
|
|
508
|
+
const next: LongshotTarget = {
|
|
509
|
+
...prev,
|
|
510
|
+
status: update.status,
|
|
511
|
+
};
|
|
512
|
+
if (update.last_error !== undefined) next.last_error = update.last_error;
|
|
513
|
+
if (update.completed_at !== undefined) next.completed_at = update.completed_at;
|
|
514
|
+
if (update.run_id !== undefined) next.run_id = update.run_id;
|
|
515
|
+
if (update.draft_count !== undefined) next.draft_count = update.draft_count;
|
|
516
|
+
if (update.incrementAttempts) next.attempts = (prev.attempts ?? 0) + 1;
|
|
517
|
+
const targets = [...state.targets];
|
|
518
|
+
targets[idx] = next;
|
|
519
|
+
return { ...state, targets };
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export interface PendingTarget {
|
|
524
|
+
target: LongshotTarget;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** Targets that still need work — pending or previously failed. */
|
|
528
|
+
export function pendingTargets(file: LongshotTargetsFile): LongshotTarget[] {
|
|
529
|
+
return file.targets.filter((t) => t.status !== "complete");
|
|
530
|
+
}
|