diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79f4cd7..9b1a497 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,51 @@ jobs: shell: pwsh run: ./skills/scripts/test-workflow-title-safety.ps1 + - name: Offline sample case contract (Windows PowerShell 5.1) + if: runner.os == 'Windows' + shell: powershell + run: | + $scratch = Join-Path $env:RUNNER_TEMP ("reverse-skill-offline-" + [guid]::NewGuid().ToString('n')) + New-Item -ItemType Directory -Force -Path $scratch | Out-Null + $sample = Join-Path $scratch 'sample.apk' + Set-Content -Path $sample -Value 'fixture' -Encoding ASCII + + ./skills/scripts/case-init.ps1 ` + -Hint "offline apk" ` + -CaseName "offline-sample" ` + -ProjectRoot $scratch ` + -Preset offline-sample ` + -Sample $sample + $scope = Join-Path $scratch 'work/offline-sample/scope.md' + $raw = Get-Content $scope -Raw + if ($raw -notmatch '(?m)^- mode: offline\r?$') { throw 'offline sample did not keep offline network mode' } + if ($raw -notmatch '(?m)^- ready_for_act: true\r?$') { throw 'offline sample did not become ready_for_act' } + ./skills/scripts/case-guard.ps1 -CaseRoot (Join-Path $scratch 'work/offline-sample') + + try { + ./skills/scripts/case-init.ps1 ` + -Hint "missing offline apk" ` + -CaseName "missing-sample" ` + -ProjectRoot $scratch ` + -Preset offline-sample ` + -Sample (Join-Path $scratch 'missing.apk') + throw 'missing offline sample was accepted' + } catch { + if ($_.Exception.Message -eq 'missing offline sample was accepted') { throw } + } + + ./skills/scripts/case-init.ps1 ` + -Hint "pending offline apk" ` + -CaseName "force-auth" ` + -ProjectRoot $scratch ` + -Sample $sample + & powershell -NoProfile -ExecutionPolicy Bypass -File ./skills/scripts/case-guard.ps1 ` + -CaseRoot (Join-Path $scratch 'work/force-auth') ` + -Force + $guardExit = $LASTEXITCODE + if ($guardExit -eq 0) { throw '-Force bypassed auth.status hard gate' } + $global:LASTEXITCODE = 0 + - name: Smoke (verify + parse + quick route) shell: pwsh run: ./skills/scripts/smoke.ps1 @@ -94,6 +139,47 @@ jobs: scratch="$(mktemp -d)" trap 'rm -rf "$scratch"' EXIT + # Fresh Linux journey: no pwsh required, artifacts stay in caller project. + caller="$scratch/caller-project" + mkdir -p "$caller" + printf 'fixture' > "$scratch/sample.apk" + ( + cd "$caller" + bash "$GITHUB_WORKSPACE/skills/scripts/master-route.sh" --hint "offline apk" + bash "$GITHUB_WORKSPACE/skills/scripts/case-init.sh" \ + --hint "offline apk" \ + --case-name "caller-default" \ + --preset offline-sample \ + --sample "$scratch/sample.apk" + ) + test -f "$caller/work/caller-default/scope.md" + grep -Eq '^- project_root: .*/caller-project$' "$caller/work/caller-default/scope.md" + grep -Eq '^- mode: offline$' "$caller/work/caller-default/scope.md" + grep -Eq '^- ready_for_act: true$' "$caller/work/caller-default/scope.md" + bash skills/scripts/case-guard.sh --case-root "$caller/work/caller-default" + test ! -e "$GITHUB_WORKSPACE/work/caller-default" + + if bash skills/scripts/case-init.sh \ + --hint "missing offline apk" \ + --case-name "missing-sample" \ + --project-root "$caller" \ + --preset offline-sample \ + --sample "$scratch/missing.apk"; then + echo "missing offline sample was accepted" >&2 + exit 1 + fi + + # Compatibility: legacy --package-root still pins the work root. + bash skills/scripts/case-init.sh \ + --hint "authorized web review" \ + --case-name "network-default" \ + --package-root "$scratch/project" \ + --auth-granted \ + --target-url "https://example.test/" + grep -Eq '^- mode: authorized_target_only$' "$scratch/project/work/network-default/scope.md" + grep -Eq '^- ready_for_act: true$' "$scratch/project/work/network-default/scope.md" + bash skills/scripts/case-guard.sh --case-root "$scratch/project/work/network-default" + if bash skills/scripts/case-init.sh \ --hint "offline apk" \ --case-name "../case-escape" \ @@ -115,16 +201,6 @@ jobs: exit 1 fi - bash skills/scripts/case-init.sh \ - --hint "authorized web review" \ - --case-name "network-default" \ - --package-root "$scratch/project" \ - --auth-granted \ - --target-url "https://example.test/" - grep -Eq '^- mode: authorized_target_only$' "$scratch/project/work/network-default/scope.md" - grep -Eq '^- ready_for_act: true$' "$scratch/project/work/network-default/scope.md" - bash skills/scripts/case-guard.sh --case-root "$scratch/project/work/network-default" - bash skills/scripts/case-init.sh \ --hint "authorized web review" \ --case-name "uppercase-network" \ @@ -151,6 +227,19 @@ jobs: exit 1 fi + # --force is compatibility-only and must not bypass the hard auth gate. + ( + cd "$caller" + bash "$GITHUB_WORKSPACE/skills/scripts/case-init.sh" \ + --hint "pending offline apk" \ + --case-name "force-auth" \ + --sample "$scratch/sample.apk" + ) + if bash skills/scripts/case-guard.sh --case-root "$caller/work/force-auth" --force; then + echo "case-guard --force bypassed auth.status hard gate" >&2 + exit 1 + fi + - name: PowerShell syntax check (PSParser, all tracked .ps1) shell: pwsh run: | diff --git a/AGENTS.md b/AGENTS.md index 8d81e4e..a9c5d9d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,37 +6,44 @@ 用户任务命中安全/逆向关键词时: -1. `skills/MASTER-ROUTING.md` 或 `powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/master-route.ps1 -Hint "<任务>"` → PRIMARY +1. `skills/MASTER-ROUTING.md` 或平台对应入口 → PRIMARY: + - Windows:`powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/master-route.ps1 -Hint "<任务>"` + - Linux / macOS / Kali:`bash skills/scripts/master-route.sh --hint "<任务>"` 2. 歧义时读 `skills/routing.md` 全矩阵(三轴:目标类型 / 用户意图 / 工具链) 3. 路由规则唯一事实源:`skills/config/routing.json`(改路由只改这里) ## 授权门禁(硬性) -- 对任何目标动手前:`powershell -File skills/scripts/case-init.ps1 -Hint "<任务>"` 生成 `work//scope.md` -- `auth.status=granted` + `network_profile` 就绪前**禁止 ACT** +- 对任何目标动手前,按平台初始化当前分析项目的 `work//scope.md`: + - Windows:`powershell -File skills/scripts/case-init.ps1 -Hint "<任务>"` + - Linux / macOS / Kali:`bash skills/scripts/case-init.sh --hint "<任务>"` +- 本地离线样本可使用 `offline-sample` preset;`auth.status=granted` + 明确 sample 才可进入 ACT。 +- `auth.status=granted` + 合法 `network_profile` / offline sample 就绪前**禁止 ACT**;`case-guard --force` / `-Force` 不得绕过这个硬门。 - 证据链:`skills/ops/evidence-finding-path.md`;角色:`skills/ops/role-map.md` ## 首次运行 -`skills/tool-index.md` 是 gitignored 的生成文件,首次使用前运行: +`skills/tool-index.md` 是 gitignored 的生成文件,首次使用前按平台运行: -```powershell -powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/refresh-tool-index.ps1 +```text +Windows: powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/refresh-tool-index.ps1 +Linux / macOS: bash skills/scripts/refresh-tool-index.sh +Kali: bash kali/scripts/refresh-tool-index.sh ``` -缺工具 → `skills/scripts/bootstrap-reverse.ps1`(清单能力,禁止猜路径)。 +缺工具 → 使用同平台 bootstrap:Windows `skills/scripts/bootstrap-reverse.ps1`;Linux / macOS `skills/scripts/bootstrap-reverse.sh`;Kali `kali/scripts/bootstrap-reverse.sh`(清单能力,禁止猜路径)。 ## 测试(改动后必跑) -```powershell -# 路由回归(routing-benchmark.json,修改 routing.json 后必跑) -powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/test-routing.ps1 +```text +Windows / PowerShell(路由回归读取 routing-benchmark.json): + powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/test-routing.ps1 + powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/verify-routing-coherence.ps1 + powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/smoke.ps1 -# 结构一致性 + 供应链 pin gate -powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/verify-routing-coherence.ps1 - -# 冒烟(verify + 脚本解析 + 快速路由) -powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/smoke.ps1 +Linux / macOS routing parity: + bash skills/scripts/test-routing.sh + bash skills/scripts/test-bootstrap-manifest.sh ``` ## 客户端边界 diff --git a/README_AI.md b/README_AI.md index 071c8fd..0816f8c 100644 --- a/README_AI.md +++ b/README_AI.md @@ -24,7 +24,7 @@ AI Community: https://linux.do ### Automatic Configuration Process ```text -0. ⚠️ Run refresh-tool-index first to generate skills/tool-index.md (see warning above) +0. ⚠️ Run the platform-native refresh-tool-index first to generate skills/tool-index.md (see warning above) 1. Detect the actual installation path of this package (derived from the location of this file, i.e., the directory containing README.md) 2. Detect the local operating system and distribution: - Windows → continue with this README and the PowerShell script path @@ -33,13 +33,15 @@ AI Community: https://linux.do - macOS → read docs/platforms/macos.md - Other / unknown → read docs/PLATFORMS.md and choose the closest deployment path 3. Follow the platform-specific deployment document to check toolchains, script entry points, MCP configuration, and path conventions -4. If the current system supports Bash, prefer the parity bootstrap entry: bash skills/scripts/bootstrap-reverse.sh ; for index refresh only, run: bash skills/scripts/refresh-tool-index.sh +4. Use the native bootstrap entry for the current platform; Linux/macOS do not require PowerShell for the core routing/case flow 5. Read RULES.md → execute all instructions inside it (CRITICAL block, global injection, precedent-auth, routing) 6. Route via skills/MASTER-ROUTING.md or: - Windows: `powershell -File skills/scripts/master-route.ps1 -Hint ""` - - Linux/macOS/Kali: open MASTER-ROUTING.md (or run the same script under pwsh if available) -7. **Ops gate (MUST):** `powershell -File skills/scripts/case-init.ps1 -Hint ""` (or hand-write `work//scope.md` per `skills/ops/scope-contract.md`). - Set `auth.status=granted` + `network_profile` before any target ACT. Evidence chain: `skills/ops/evidence-finding-path.md`. Roles: `skills/ops/role-map.md`. Identity: `skills/ops/IDENTITY.md`. + - Linux/macOS/Kali: `bash skills/scripts/master-route.sh --hint ""` +7. **Ops gate (MUST):** initialize the case using the native platform entry: + - Windows: `powershell -File skills/scripts/case-init.ps1 -Hint ""` + - Linux/macOS/Kali: `bash skills/scripts/case-init.sh --hint ""` + Both default case artifacts to the caller analysis project's `work//`. Set `auth.status=granted` + a valid network profile before target ACT. An authorized local sample may remain `offline` when an explicit sample is supplied through the `offline-sample` preset. `-Force`/`--force` never bypasses the scope hard gate. Evidence chain: `skills/ops/evidence-finding-path.md`. Roles: `skills/ops/role-map.md`. Identity: `skills/ops/IDENTITY.md`. 8. Open PRIMARY SKILL.md → execute ACTION REQUIRED. Append timeline/workitems under the case dir. 9. Before report handoff, run `python3 skills/case-review/scripts/review_case.py work/ --verify-hashes --strict` and resolve every error. 10. Continue the behavior chain → report via docs-generator + field-journal. @@ -49,10 +51,10 @@ AI Community: https://linux.do | Detection result | Signal | Required deployment document | Detection / deployment entry | |---|---|---|---| -| Windows | PowerShell, `$env:OS`, Windows paths | `README_AI.md` | `skills/scripts/*.ps1` | -| Kali Linux | `/etc/os-release` contains `kali` | `kali/README-kali.md` | `kali/scripts/bootstrap-reverse.sh` / `kali/scripts/refresh-tool-index.sh` | -| Ubuntu / Debian / Mint / Pop!_OS | `/etc/os-release` contains the distro ID | `docs/platforms/linux.md` | `skills/scripts/bootstrap-reverse.sh` / `skills/scripts/refresh-tool-index.sh` | -| macOS | `uname -s` = `Darwin` | `docs/platforms/macos.md` | `skills/scripts/bootstrap-reverse.sh` / `skills/scripts/refresh-tool-index.sh` | +| Windows | PowerShell, `$env:OS`, Windows paths | `README_AI.md` | `skills/scripts/master-route.ps1` / `case-init.ps1` / `bootstrap-reverse.ps1` / `refresh-tool-index.ps1` | +| Kali Linux | `/etc/os-release` contains `kali` | `kali/README-kali.md` | `skills/scripts/master-route.sh` / `case-init.sh`; Kali bootstrap/refresh under `kali/scripts/` | +| Ubuntu / Debian / Mint / Pop!_OS | `/etc/os-release` contains the distro ID | `docs/platforms/linux.md` | `skills/scripts/master-route.sh` / `case-init.sh` / `bootstrap-reverse.sh` / `refresh-tool-index.sh` | +| macOS | `uname -s` = `Darwin` | `docs/platforms/macos.md` | `skills/scripts/master-route.sh` / `case-init.sh` / `bootstrap-reverse.sh` / `refresh-tool-index.sh` | | Other / unknown | Cannot identify confidently | `docs/PLATFORMS.md` | Choose the closest platform, then continue | > RULES.md contains the full behavior chain (steps 0–14). After reading this file, first complete OS detection and platform-document routing, then read RULES.md and start executing. Do not stop at "configuration completed." @@ -74,7 +76,7 @@ AI Community: https://linux.do **Tool index**: **Rules written to**: **Note**: Future reverse-engineering / penetration-testing / security tasks will be routed automatically. Missing tools will be installed automatically when needed. -**Ops**: Before target ACT, ensure case scope (`case-init` / `ops/scope-contract`) has auth granted + network_profile. +**Ops**: Before target ACT, ensure case scope (`case-init` / `ops/scope-contract`) has auth granted + a valid network/offline-sample profile. ``` --- @@ -92,18 +94,21 @@ It solves two problems: | Platform | Status | Entry | |---|---|---| -| Windows | Full primary path | `README_AI.md`, PowerShell scripts | -| Kali Linux | Specialized support | `kali/README-kali.md`, `kali/scripts/bootstrap-reverse.sh`, `kali/scripts/refresh-tool-index.sh` | -| Ubuntu / Debian Linux | Generic support | `docs/platforms/linux.md`, `skills/scripts/bootstrap-reverse.sh`, `skills/scripts/refresh-tool-index.sh` | -| macOS | Generic support | `docs/platforms/macos.md`, `skills/scripts/bootstrap-reverse.sh`, `skills/scripts/refresh-tool-index.sh` | +| Windows | Full primary path | `README_AI.md`, `skills/scripts/master-route.ps1`, `case-init.ps1`, PowerShell bootstrap/refresh | +| Kali Linux | Specialized support | `skills/scripts/master-route.sh`, `case-init.sh`, `kali/README-kali.md`, Kali bootstrap/refresh | +| Ubuntu / Debian Linux | Generic support | `docs/platforms/linux.md`, `skills/scripts/master-route.sh`, `case-init.sh`, Bash bootstrap/refresh | +| macOS | Generic support | `docs/platforms/macos.md`, `skills/scripts/master-route.sh`, `case-init.sh`, Bash bootstrap/refresh | -Generic Linux/macOS users can list bootstrap capabilities with: +Generic Linux/macOS users can run the core routing/case path without installing PowerShell: ```bash +bash skills/scripts/master-route.sh --hint "offline apk" +bash skills/scripts/case-init.sh --hint "offline apk" --case-name my-sample --preset offline-sample --sample ./app.apk +bash skills/scripts/case-guard.sh --case-root work/my-sample bash skills/scripts/bootstrap-reverse.sh --list ``` -Kali users should use the dedicated Kali entrypoint: +Kali users should use the dedicated Kali bootstrap entrypoint: ```bash bash kali/scripts/bootstrap-reverse.sh @@ -272,10 +277,12 @@ Full dependency table with paths in the original [README.md](README.md). ### Refresh the Tool Index -Do not trust someone else's scan result for long. After migrating to a new machine, refresh it first: +Do not trust someone else's scan result for long. After migrating to a new machine, refresh it first with the native platform entry: -```powershell -powershell -File "\skills\scripts\refresh-tool-index.ps1" +```text +Windows: powershell -File "\skills\scripts\refresh-tool-index.ps1" +Linux / macOS: bash /skills/scripts/refresh-tool-index.sh +Kali: bash /kali/scripts/refresh-tool-index.sh ``` After success, check: @@ -403,19 +410,23 @@ The key is to inject: package path, key entry files, MCP addresses, and "route f If you have configured `.claude\settings.local.json` or `.claude\scripts\route-reverse.ps1`, update all old paths after migration. ### Tool Index -After migration, run again: -```powershell -powershell -File "\skills\scripts\refresh-tool-index.ps1" +After migration, run the native refresh command again: + +```text +Windows: powershell -File "\skills\scripts\refresh-tool-index.ps1" +Linux / macOS: bash /skills/scripts/refresh-tool-index.sh +Kali: bash /kali/scripts/refresh-tool-index.sh ``` --- ## Recommended Verification Checklist -```powershell +Core runtime checks: + +```text java -version -python --version -pip --version +python --version # or python3 --version node -v npx -v jadx --version @@ -424,14 +435,28 @@ adb version frida-ps -U ``` -IDA chain: +Core routing/case contract: + +```text +Windows: + powershell -File skills/scripts/master-route.ps1 -Hint "offline apk" + powershell -File skills/scripts/case-init.ps1 -Hint "offline apk" -CaseName verify-sample -Preset offline-sample -Sample ".\sample.apk" + +Linux / macOS / Kali: + bash skills/scripts/master-route.sh --hint "offline apk" + bash skills/scripts/case-init.sh --hint "offline apk" --case-name verify-sample --preset offline-sample --sample ./sample.apk +``` + +IDA chain on Windows: ```powershell powershell -File "\ida-reverse\scripts\start.ps1" ``` Tool index: -```powershell -powershell -File "\skills\scripts\refresh-tool-index.ps1" +```text +Windows: powershell -File "\skills\scripts\refresh-tool-index.ps1" +Linux / macOS: bash /skills/scripts/refresh-tool-index.sh +Kali: bash /kali/scripts/refresh-tool-index.sh ``` --- @@ -512,7 +537,7 @@ When the AI tries to auto-complete installation and still fails, it **must not s ### AI Failure-Handling Flow ```text -1. Call bootstrap-reverse.ps1 to attempt automatic installation +1. Call the platform-native bootstrap (`bootstrap-reverse.ps1` on Windows, `bootstrap-reverse.sh` on Linux/macOS, Kali bootstrap under `kali/scripts/`) 2. Verify whether the tool is usable after installation 3. If it is still unavailable → do not retry → immediately output structured guidance ``` @@ -577,4 +602,4 @@ This project (`reverse-skill`) is primarily licensed under the **MIT License**. This package is intended only for legally authorized security research, learning, and CTF competitions. - Users must ensure all operations are within legal boundaries - Unauthorized penetration testing against other people's systems is illegal -- The package author is not responsible for misuse +- The package author is not responsible for misuse \ No newline at end of file diff --git a/RULES.md b/RULES.md index 9fbe7a0..de17234 100644 --- a/RULES.md +++ b/RULES.md @@ -12,11 +12,13 @@ After reading this file, you MUST NOW (immediately) execute: -``` +```text 1. NOW: This file's directory is the package root -2. NOW: Run skills/scripts/master-route.ps1 -Hint "" → PRIMARY (SSoT: skills/config/routing.json) -3. NEXT: case-init.ps1 until scope.md has auth.status=granted and a legal network_profile. Mentioning a target is NOT granted. -4. ACT: Open PRIMARY SKILL.md and execute ACTION REQUIRED. Tools only from tool-index.md; missing → bootstrap-reverse.ps1 +2. NOW: Run the platform-native router → PRIMARY (SSoT: skills/config/routing.json) + - Windows: powershell -File skills/scripts/master-route.ps1 -Hint "" + - Linux/macOS/Kali: bash skills/scripts/master-route.sh --hint "" +3. NEXT: Run the platform-native case-init until scope.md has auth.status=granted plus a legal network_profile, or an explicitly authorized offline-sample scope is ready. Mentioning a target is NOT granted. -Force/--force never bypasses the gate. +4. ACT: Open PRIMARY SKILL.md and execute ACTION REQUIRED. Tools only from tool-index.md; missing → platform-native bootstrap. Optional later (do NOT preload): - Ambiguous PRIMARY → skills/routing.md (advisory matrix only) @@ -27,7 +29,7 @@ Optional later (do NOT preload): IMPORTANT — Shared installation: - tool-index.md is the SINGLE SOURCE OF TRUTH for tool availability - If another CLI already installed tools (tool-index shows "yes"), DO NOT reinstall -- Only run refresh-tool-index.ps1 if you suspect tool-index is stale (e.g., user says "I just installed X") +- Only run the platform-native refresh-tool-index if you suspect tool-index is stale (e.g., user says "I just installed X") - Only run bootstrap for tools that are BOTH needed AND marked "no" Conditional reads (load only when needed, do NOT preload): @@ -147,19 +149,19 @@ Hot path only: ## Canonical Behavior Chain (All other files reference THIS version) -``` +```text 1. Identify task as security/reverse type → trigger this routing rule 2. Detect package root path (derive from this file's location) -3. master-route.ps1 -Hint → PRIMARY from skills/config/routing.json -4. case-init.ps1 / scope.md (ops/scope-contract) — auth.status=granted + network_profile before any target ACT. precedent-auth.md does not grant. +3. Platform-native master-route (`.ps1` Windows / `.sh` Linux, macOS, Kali) → PRIMARY from skills/config/routing.json; use routing.md only when ambiguous +4. Platform-native case-init / scope.md (ops/scope-contract) — auth.status=granted + valid network profile, or explicit authorized offline sample, before any target ACT; Force never bypasses the hard gate 5. Open PRIMARY SKILL.md ACTION REQUIRED 6. Route not matched → propose new skill (edit routing.json + benchmark; do not hand-edit routing.md as SSoT) -7. tool-index.md → real paths; missing (first run) → template + refresh-tool-index -8. Missing tools → platform bootstrap (Windows ps1 / Linux sh / Kali sh) -9. Execute PRIMARY workflow (timeline/workitems; Evidence→Finding→Path) +7. Read tool-index.md → confirm local tool status. If missing (first run) → template + platform-native refresh-tool-index +8. Missing tools → platform bootstrap + refresh (Windows ps1 / Linux sh / Kali sh) +9. Enter skill workflow → execute (timeline/workitems; Evidence→Finding→Path per ops/) 10. Continuously report progress (do NOT go silent) -13. Task complete → Completion Checklist (report must include Evidence chain) -14. Output final results +11. Task complete → Completion Checklist (report must include Evidence chain) +12. Output final results ``` --- @@ -225,7 +227,6 @@ After task completion (vulnerability verified / reverse complete / flag captured | "IAT repair keeps failing; I'll grind more static unpackers" | **IAT repair iron rule:** try auto/semi-auto repair first; on tool error or unreable binary after repair, STOP static IAT, record E-iat-repair-fail, switch to dynamic API breakpoints. FORBIDDEN infinite static IAT thrash. | | "No import table (.NET) so the hard gate does not apply" | **Equivalent anchor still MUST:** .NET → dnSpy/IL/metadata summary into E-imports slot; DLL/SYS → E-exports alongside imports. FORBIDDEN to skip the gate. | - --- ## Self-Audit Before Claiming "Complete" @@ -271,7 +272,7 @@ Before saying "task complete" or "done", MUST self-check: ## Context Window Layout Rules (Attention Optimization) LLM attention distribution (high→low): -``` +```text [First 10%] ████████████ ← Highest attention — put "immediate action" instructions here [Middle 80%] ████░░░░░░░░ ← Attention decays — put reference materials here [Last 10%] ████████████ ← Attention recovers — put "MUST NOT skip" and Checklist here @@ -291,7 +292,7 @@ When tool parameters MUST be passed exactly as given, use opaque identifiers (co - **MUST NOT**: Let Agent freely rewrite semantic parameters (e.g., changing strict/deny to lenient synonyms) Example: -``` +```text alpha -> --scope authorized-only beta -> --approval required gamma -> --destructive false @@ -383,15 +384,17 @@ bash /kali/scripts/refresh-tool-index.sh ### Post-Trigger Execution (Compact — do NOT re-run first-time setup!) -``` -1. NOW: Run /skills/scripts/master-route.ps1 -Hint "" → PRIMARY -2. NEXT: case-init.ps1 / scope.md — auth.status=granted + network_profile; MUST NOT ACT against targets until ready -3. ACT: Open PRIMARY SKILL.md +```text +1. NOW: Run the platform-native master-route (.ps1 on Windows / .sh on Linux, macOS, Kali) → PRIMARY from routing.json +2. NEXT: If ambiguous, open /skills/routing.md +3. NEXT: Use platform-native case-init / scope.md — set auth.status=granted + valid network profile, or an explicit authorized offline-sample scope; Force never bypasses the hard gate +4. ACT: Open PRIMARY SKILL.md; timeline/workitems + Evidence→Finding→Path (ops/*) ``` ### Core Rules (Compact) -- **MUST**: case scope (case-init / ops/scope-contract) before ACT; auth.status=granted + network_profile required +- **MUST**: case scope (platform-native case-init / ops/scope-contract) before ACT; auth.status=granted + valid network/offline-sample scope required +- **MUST**: `-Force` / `--force` never bypasses authorization, scope, network, or readiness gates - **MUST**: Missing tools → bootstrap, NEVER guess paths - **MUST NOT**: Treat precedent-auth.md or "user named a target" as granted - **MUST NOT**: Reply "understood, tell me your task" after reading rules diff --git a/skills/MASTER-ROUTING.md b/skills/MASTER-ROUTING.md index c9b0396..d0536ff 100644 --- a/skills/MASTER-ROUTING.md +++ b/skills/MASTER-ROUTING.md @@ -1,6 +1,6 @@ # reverse-skill PRIMARY 快路径 -> 与 `scripts/master-route.ps1` 保持一致。 +> `scripts/master-route.ps1` 与 `scripts/master-route.sh` 必须保持相同路由契约;平台只改变执行入口,不改变 routing semantics。 ## 执行契约 @@ -15,6 +15,8 @@ 8. 未命中 → 读 routing.md 全表或提议新 skill ``` +### Windows + ```powershell powershell -File skills\scripts\master-route.ps1 -Hint "<用户任务>" # 默认写出当前项目的 work/master-route-/route-scope.md;从其他目录调用时显式指定项目根 @@ -24,15 +26,36 @@ powershell -File skills\scripts\case-init.ps1 -Hint "<用户任务>" -CaseName " powershell -File skills\scripts\case-init.ps1 -Hint "<用户任务>" -CaseName "my-case" -ProjectRoot "C:\path\to\analysis-project" # 一次成型可 ACT(授权 + 目标 + 网络档): powershell -File skills\scripts\case-init.ps1 -Hint "<任务>" -CaseName "my-case" -AuthGranted -TargetUrl "https://target/" -NetworkProfile authorized_target_only +# 本地离线样本: +powershell -File skills\scripts\case-init.ps1 -Hint "offline apk" -CaseName "my-sample" -Preset offline-sample -Sample ".\app.apk" # 冒烟:verify + 脚本解析 + 路由矩阵(含中文 Hint) powershell -File skills\scripts\smoke.ps1 -# ACT 前轻量 scope 门禁(未就绪 exit 2;-Force 仅警告) +# ACT 前轻量 scope 门禁(未就绪 exit 2;-Force 为兼容参数,不能绕过硬门) powershell -File skills\scripts\case-guard.ps1 -CaseRoot work\my-case # Evidence 追加 powershell -File skills\scripts\append-evidence.ps1 -CaseRoot work\my-case -Id E-001 -Title "..." -ReproCommand "..." python3 skills/case-review/scripts/review_case.py work/ --verify-hashes --strict ``` +### Linux / macOS / Kali + +不要求为了核心 route/case 流程安装 PowerShell: + +```bash +bash skills/scripts/master-route.sh --hint "<用户任务>" +bash skills/scripts/master-route.sh --hint "<用户任务>" --project-root "/path/to/analysis-project" +bash skills/scripts/case-init.sh --hint "<用户任务>" --case-name "my-case" +bash skills/scripts/case-init.sh --hint "<用户任务>" --case-name "my-case" --project-root "/path/to/analysis-project" +# 本地离线样本: +bash skills/scripts/case-init.sh --hint "offline apk" --case-name "my-sample" --preset offline-sample --sample ./app.apk +# ACT 前轻量 scope 门禁(--force 为兼容参数,不能绕过硬门): +bash skills/scripts/case-guard.sh --case-root work/my-sample +# 路由 parity: +bash skills/scripts/test-routing.sh +bash skills/scripts/test-bootstrap-manifest.sh +python3 skills/case-review/scripts/review_case.py work/ --verify-hashes --strict +``` + ## 作战契约(ops) | 文档 | 用途 | diff --git a/skills/SKILL.md b/skills/SKILL.md index 9c21354..368ed6d 100644 --- a/skills/SKILL.md +++ b/skills/SKILL.md @@ -10,10 +10,10 @@ description: Routes reverse engineering, exploitation, penetration testing, malw 读完本文件后,不允许只回复“已读/已理解”。必须按顺序执行: -1. `NOW`:跑 `scripts/master-route.ps1 -Hint "..."`(SSoT:`config/routing.json`)定 PRIMARY。 -2. `NOW`:`scripts/case-init.ps1` 落地 `work//scope.md`;**auth 未 granted 禁止对目标 ACT**。点名目标 ≠ granted。 +1. `NOW`:跑平台原生 router(Windows `scripts/master-route.ps1`;Linux/macOS/Kali `scripts/master-route.sh`),从 `config/routing.json` 定 PRIMARY;疑难再读 `routing.md` 三轴附录。 +2. `NOW`:平台原生 `case-init` 落地当前分析项目的 `work//scope.md`;**auth 未 granted 禁止对目标 ACT**。本地离线样本使用 `offline-sample` preset + explicit sample;Force 不得绕过硬门。 3. `ACT`:立即打开 PRIMARY `SKILL.md` 执行 ACTION REQUIRED。 -4. `NEXT`:工具路径只认 `tool-index.md`;缺工具 → `bootstrap-reverse.ps1`(仅 manifest)。 +4. `NEXT`:工具路径只认 `tool-index.md`;缺工具 → 平台原生 bootstrap(仅 manifest)。 5. 结论用 Evidence→Finding→Path。报告/journal 是 SHOULD,除非用户要交付物。 **身份**:见 `ops/IDENTITY.md`(轻量路由包 + 工具自举 + journal;**不是** Z3r0 式平台)。 @@ -82,10 +82,10 @@ description: Routes reverse engineering, exploitation, penetration testing, malw 遇到逆向、CTF、抓包、前端签名、APK 改包、二进制分析类任务时,先按这个顺序进入: -1. `scripts/master-route.ps1` → PRIMARY(`config/routing.json`) -2. `case-init.ps1` → `scope.md` +1. 平台原生 router(Windows `scripts/master-route.ps1`;Linux/macOS/Kali `scripts/master-route.sh`)→ PRIMARY(`config/routing.json`) +2. 平台原生 `case-init` → `scope.md` 3. 打开 PRIMARY `SKILL.md` -4. 需要本机路径时再读 `tool-index.md` +4. 疑难时读 `routing.md`,需要本机路径时读 `tool-index.md` ## 工作思路 @@ -136,12 +136,23 @@ description: Routes reverse engineering, exploitation, penetration testing, malw ## 按需自举 -当 workflow 发现缺少工具时,不要直接报错。统一调用: +当 workflow 发现缺少工具时,不要直接报错。统一调用平台原生 bootstrap: +Windows: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File "\scripts\bootstrap-reverse.ps1" -Capability @('工具名') -StartServices ``` +Linux / macOS: +```bash +bash /scripts/bootstrap-reverse.sh 工具名 --start-services +``` + +Kali: +```bash +bash /kali/scripts/bootstrap-reverse.sh 工具名 --start-services +``` + 支持的能力(以 `scripts/bootstrap-manifest.json` 为准):jadx、apktool、jeb-pro、frida、frida-ps、idalib-mcp、reqable-mcp、jshookmcp、anything-analyzer、idapro、r2、rabin2、adb、agent-browser、ghidra-mcp、seclists、proxycat、burpsuite-mcp、nmap、pentestswarm、binwalk、yara、pwntools、bkcrack > JEB Pro 已登记为**手动许可安装**能力:bootstrap 只输出指引,绝不下载或规避商业许可。Reqable MCP 仅登记固定版本的官方运行时,仍需要用户自行安装 Reqable 桌面客户端。 diff --git a/skills/ops/scope-contract.md b/skills/ops/scope-contract.md index ced078c..a65dc76 100644 --- a/skills/ops/scope-contract.md +++ b/skills/ops/scope-contract.md @@ -1,17 +1,38 @@ # 通用 Scope 契约(任务启动硬门槛) -> **MUST**:任何安全/逆向/渗透任务在 **ACT 之前** 在用户项目或 `work//` 落地 `scope.md`。 -> 无 scope → 只允许读文档/路由,**禁止** 对目标主动扫描、Hook、利用。 +> **MUST**:任何安全/逆向/渗透任务在 **ACT 之前** 在当前用户分析项目的 `work//` 落地 `scope.md`。 +> 无 scope → 只允许读文档/路由,**禁止** 对目标主动扫描、Hook、利用。 > 模板可复制;字段名保持英文键,便于脚本校验。 ## 如何初始化 +Windows: + ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File skills\scripts\case-init.ps1 -Hint "<任务一句话>" -CaseName "my-case" # 默认产出:当前分析项目的 work//scope.md 等 # 从其他目录调用 skill 时显式指定:-ProjectRoot "C:\path\to\analysis-project" + +# 合法本地离线样本:auth granted + offline + explicit sample → ready_for_act=true +powershell -NoProfile -ExecutionPolicy Bypass -File skills\scripts\case-init.ps1 ` + -Hint "offline apk" -CaseName "my-sample" -Preset offline-sample -Sample ".\app.apk" ``` +Linux / macOS / Kali: + +```bash +bash skills/scripts/case-init.sh --hint "<任务一句话>" --case-name "my-case" +# 默认产出:caller 当前分析项目的 work//scope.md 等 +# 从其他目录调用时显式指定:--project-root "/path/to/analysis-project" + +# 合法本地离线样本 +bash skills/scripts/case-init.sh \ + --hint "offline apk" --case-name "my-sample" \ + --preset offline-sample --sample ./app.apk +``` + +`-PackageRoot` / `--package-root` 保留为兼容参数;新流程应以 `ProjectRoot` / `--project-root` 表示 case artifact 的归属项目。 + ## scope.md 完整模板 ```markdown @@ -21,6 +42,7 @@ powershell -NoProfile -ExecutionPolicy Bypass -File skills\scripts\case-init.ps1 - case_id: {YYYYMMDD-short} - created: {ISO-8601} - operator: {name or local} +- project_root: {caller analysis project} - primary_skill: {from master-route} - lead_role: lead # see ops/role-map.md - specialist_roles: [] # e.g. cie, cpe, cre @@ -73,11 +95,13 @@ powershell -NoProfile -ExecutionPolicy Bypass -File skills\scripts\case-init.ps1 ```text RULES / MASTER-ROUTING / SKILL: 1) master-route → PRIMARY - 2) case-init 或手写 scope.md + 2) 平台原生 case-init 或手写 scope.md 3) auth 未 granted → STOP,只允许补授权材料 4) ready_for_act = true → 打开 PRIMARY SKILL.md → ACT ``` +`case-guard -Force` / `case-guard --force` 是兼容参数,**不得**绕过 `auth.status`、合法 scope、network profile 或 `ready_for_act` 硬门。 + ## network_profile 速查 | mode | 允许 | 禁止 | diff --git a/skills/scripts/case-guard.ps1 b/skills/scripts/case-guard.ps1 index 8f2a91d..1002cc5 100644 --- a/skills/scripts/case-guard.ps1 +++ b/skills/scripts/case-guard.ps1 @@ -2,7 +2,7 @@ # Lightweight scope gate before ACT. Exit 0 = ok, 2 = not ready, 1 = usage/error. # Usage: # powershell -File skills/scripts/case-guard.ps1 -CaseRoot work\my-case -# powershell -File skills/scripts/case-guard.ps1 -CaseRoot work\my-case -Force # warn but exit 0 +# powershell -File skills/scripts/case-guard.ps1 -CaseRoot work\my-case -Force # compatibility flag; never bypasses scope hard gates param( [Parameter(Mandatory = $true)] [string] $CaseRoot, @@ -94,9 +94,8 @@ Write-Host ("CASE-GUARD NOT READY: {0}" -f $CaseRoot) -ForegroundColor Yellow foreach ($i in $issues) { Write-Host (" - {0}" -f $i) -ForegroundColor Yellow } if ($Force) { - Write-Host 'CASE-GUARD: -Force set; continuing with warnings only.' -ForegroundColor Yellow - exit 0 + Write-Host 'CASE-GUARD: -Force does not bypass scope hard gates.' -ForegroundColor Yellow } -Write-Host 'Fix scope (or re-run case-init -AuthGranted -TargetUrl ...) or pass -Force.' -ForegroundColor Yellow +Write-Host 'Fix scope (or re-run case-init -AuthGranted -TargetUrl ...).' -ForegroundColor Yellow exit 2 diff --git a/skills/scripts/case-guard.sh b/skills/scripts/case-guard.sh index 8a6e050..b873b78 100755 --- a/skills/scripts/case-guard.sh +++ b/skills/scripts/case-guard.sh @@ -2,7 +2,7 @@ # Lightweight scope gate before ACT. Exit 0=ok, 2=not ready, 1=usage/error. # Usage: # bash skills/scripts/case-guard.sh --case-root work/my-case -# bash skills/scripts/case-guard.sh --case-root work/my-case --force +# bash skills/scripts/case-guard.sh --case-root work/my-case --force # compatibility flag; never bypasses scope hard gates set -euo pipefail CASE_ROOT="" @@ -120,9 +120,8 @@ echo "CASE-GUARD NOT READY: $CASE_ROOT" for i in "${ISSUES[@]}"; do echo " - $i"; done if [[ $FORCE -eq 1 ]]; then - echo "CASE-GUARD: --force set; continuing with warnings only." - exit 0 + echo "CASE-GUARD: --force does not bypass scope hard gates." fi -echo "Fix scope (or re-run case-init with --auth-granted --target-url ...) or pass --force." +echo "Fix scope (or re-run case-init with --auth-granted --target-url ...)." exit 2 diff --git a/skills/scripts/case-init.ps1 b/skills/scripts/case-init.ps1 index 447f986..304e07a 100644 --- a/skills/scripts/case-init.ps1 +++ b/skills/scripts/case-init.ps1 @@ -4,6 +4,9 @@ # Ready-to-act example: # powershell -File skills/scripts/case-init.ps1 -Hint "web pentest" -CaseName my-case ` # -AuthGranted -TargetUrl "https://app.example/" -NetworkProfile authorized_target_only +# Offline sample ready-to-act example: +# powershell -File skills/scripts/case-init.ps1 -Hint "offline apk" -CaseName my-sample ` +# -Preset offline-sample -Sample ".\app.apk" param( [string] $Hint = '', [string] $CaseName = '', @@ -15,6 +18,8 @@ param( [string] $AuthBasis = 'own_system', [string] $EvidenceOfAuth = '', [string] $TargetUrl = '', + [string] $Sample = '', + [string] $Preset = '', [string[]] $InScopeAssets = @(), [string] $NetworkProfile = '', [switch] $ReadyForAct @@ -37,6 +42,43 @@ $requestedProjectRoot = if (-not [string]::IsNullOrWhiteSpace($ProjectRoot)) { } $projectRoot = Resolve-ReverseProjectRoot -RequestedRoot $requestedProjectRoot +if (-not [string]::IsNullOrWhiteSpace($Sample)) { + try { + $Sample = [System.IO.Path]::GetFullPath($Sample) + } catch { + throw "Invalid -Sample path '$Sample': $($_.Exception.Message)" + } + if (-not (Test-Path -LiteralPath $Sample -PathType Leaf)) { + throw "Local -Sample file not found: $Sample" + } +} + +# Cross-platform case presets. Keep semantics aligned with case-init.sh. +$presetNormalized = $Preset.Trim().ToLowerInvariant() +if ($presetNormalized -in @('offline-sample', 'own-sample', 'local-sample')) { + $AuthGranted = $true + $AuthStatus = 'granted' + $AuthBasis = 'own_system' + if ([string]::IsNullOrWhiteSpace($NetworkProfile)) { $NetworkProfile = 'offline' } + if ([string]::IsNullOrWhiteSpace($EvidenceOfAuth)) { + $EvidenceOfAuth = 'preset:offline-sample (owner-operated local file)' + } +} elseif ($presetNormalized -in @('ctf-public', 'ctf')) { + $AuthGranted = $true + $AuthStatus = 'granted' + $AuthBasis = 'ctf_public' + if ([string]::IsNullOrWhiteSpace($NetworkProfile)) { $NetworkProfile = 'authorized_target_only' } + if ([string]::IsNullOrWhiteSpace($EvidenceOfAuth)) { $EvidenceOfAuth = 'preset:ctf-public' } +} elseif ($presetNormalized -in @('own-system', 'lab-only')) { + $AuthGranted = $true + $AuthStatus = 'granted' + $AuthBasis = 'own_system' + if ([string]::IsNullOrWhiteSpace($NetworkProfile)) { $NetworkProfile = 'lab_only' } + if ([string]::IsNullOrWhiteSpace($EvidenceOfAuth)) { $EvidenceOfAuth = 'preset:own-system/lab' } +} elseif (-not [string]::IsNullOrWhiteSpace($Preset)) { + Write-Host ("WARN: unknown -Preset '{0}' (allowed: offline-sample|ctf-public|own-system)" -f $Preset) -ForegroundColor Yellow +} + if (-not $CaseName) { $slug = if ($Hint) { ($Hint.ToLowerInvariant() -replace '[^a-z0-9]+', '-').Trim('-') @@ -92,6 +134,9 @@ $evidenceAuth = if (-not [string]::IsNullOrWhiteSpace($EvidenceOfAuth)) { $assets = New-Object System.Collections.Generic.List[string] if (-not [string]::IsNullOrWhiteSpace($TargetUrl)) { [void]$assets.Add($TargetUrl.Trim()) } +if (-not [string]::IsNullOrWhiteSpace($Sample) -and -not $assets.Contains($Sample.Trim())) { + [void]$assets.Add($Sample.Trim()) +} foreach ($a in @($InScopeAssets)) { if (-not [string]::IsNullOrWhiteSpace($a) -and -not $assets.Contains($a.Trim())) { [void]$assets.Add($a.Trim()) @@ -105,8 +150,8 @@ if ($assets.Count -eq 0 -and $Hint -match 'https?://([^\s/]+)') { $networkMode = 'offline' if (-not [string]::IsNullOrWhiteSpace($NetworkProfile)) { $networkMode = $NetworkProfile.Trim() -} elseif ($assets.Count -gt 0 -and $authStatusResolved -eq 'granted') { - # training labs / intentional vulns often use lab_only; default authorized_target_only +} elseif ($assets.Count -gt 0 -and $authStatusResolved -eq 'granted' -and [string]::IsNullOrWhiteSpace($Sample)) { + # Authorized network targets default to target-only. Explicit local samples remain offline. $networkMode = 'authorized_target_only' } # normalize common aliases @@ -124,19 +169,21 @@ if ($networkMode -notin $allowedNetworkModes) { throw "Invalid -NetworkProfile '$NetworkProfile'. Allowed: offline, lab_only, authorized_target_only, unrestricted_lab (aliases: lab, authorized, auth, offline_only)." } -# ready_for_act requires auth granted + assets + non-offline network. -# -ReadyForAct cannot skip auth (would bypass hard gate). +# ready_for_act requires auth granted + assets. Network targets need a non-offline +# profile; an explicit local sample is valid in offline mode. -ReadyForAct never +# bypasses auth or scope. $ready = $false $netAllowsAct = ($networkMode -ne 'offline' -and -not [string]::IsNullOrWhiteSpace($networkMode)) -if ($authStatusResolved -eq 'granted' -and $assets.Count -gt 0 -and $netAllowsAct) { +$offlineSampleReady = ($networkMode -eq 'offline' -and -not [string]::IsNullOrWhiteSpace($Sample) -and $assets.Count -gt 0) +if ($authStatusResolved -eq 'granted' -and $assets.Count -gt 0 -and ($netAllowsAct -or $offlineSampleReady)) { $ready = $true } elseif ($ReadyForAct) { if ($authStatusResolved -ne 'granted') { Write-Host 'WARN: -ReadyForAct ignored because auth.status is not granted' -ForegroundColor Yellow } elseif ($assets.Count -eq 0) { Write-Host 'WARN: -ReadyForAct ignored because in_scope.assets is empty' -ForegroundColor Yellow - } elseif (-not $netAllowsAct) { - Write-Host 'WARN: -ReadyForAct ignored because network_profile is offline/empty' -ForegroundColor Yellow + } elseif (-not $netAllowsAct -and -not $offlineSampleReady) { + Write-Host 'WARN: -ReadyForAct ignored because offline mode requires an explicit -Sample' -ForegroundColor Yellow } } @@ -197,6 +244,7 @@ $scope = @" - lead_role: lead - specialist_roles: [] - hint: $Hint +- preset: $(if ([string]::IsNullOrWhiteSpace($Preset)) { 'none' } else { $Preset }) ## auth - status: $authStatusResolved @@ -297,7 +345,9 @@ $readmeNext = if ($ready) { "@ } else { @" -1. Edit ``scope.md`` — set auth.status=granted and in_scope (or re-run with -AuthGranted -TargetUrl) +1. Edit ``scope.md`` — set auth.status=granted and in_scope + - network target: re-run with ``-AuthGranted -TargetUrl `` + - local sample: re-run with ``-Preset offline-sample -Sample `` 2. Set ready_for_act when checklist complete 3. Open primary skill: skills/$primary 4. Append ``timeline.md``; update ``workitems.md`` diff --git a/skills/scripts/case-init.sh b/skills/scripts/case-init.sh index c3b4c6c..eecc473 100755 --- a/skills/scripts/case-init.sh +++ b/skills/scripts/case-init.sh @@ -9,6 +9,8 @@ set -euo pipefail HINT="" CASE_NAME="" PACKAGE_ROOT="" +PROJECT_ROOT="" +PACKAGE_ROOT_BOUND=0 AUTH_STATUS="" AUTH_GRANTED=0 AUTH_BASIS="unknown" @@ -23,7 +25,8 @@ while [[ $# -gt 0 ]]; do case "$1" in -Hint|--hint) HINT="${2:-}"; shift 2 ;; -CaseName|--case-name) CASE_NAME="${2:-}"; shift 2 ;; - -PackageRoot|--package-root) PACKAGE_ROOT="${2:-}"; shift 2 ;; + -PackageRoot|--package-root) PACKAGE_ROOT="${2:-}"; PACKAGE_ROOT_BOUND=1; shift 2 ;; + -ProjectRoot|--project-root) PROJECT_ROOT="${2:-}"; shift 2 ;; -AuthStatus|--auth-status) AUTH_STATUS="${2:-}"; shift 2 ;; -AuthGranted|--auth-granted) AUTH_GRANTED=1; shift ;; -AuthBasis|--auth-basis) AUTH_BASIS="${2:-}"; shift 2 ;; @@ -46,6 +49,22 @@ SKILLS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" if [[ -z "$PACKAGE_ROOT" ]]; then PACKAGE_ROOT="$(cd "$SKILLS_ROOT/.." && pwd)" fi +if [[ -z "$PROJECT_ROOT" ]]; then + if [[ $PACKAGE_ROOT_BOUND -eq 1 ]]; then + PROJECT_ROOT="$PACKAGE_ROOT" + else + PROJECT_ROOT="$(pwd -P)" + fi +fi + +if [[ -n "$SAMPLE" ]]; then + if [[ ! -f "$SAMPLE" ]]; then + echo "Local --sample file not found: $SAMPLE" >&2 + exit 2 + fi + sample_dir="$(cd "$(dirname "$SAMPLE")" && pwd -P)" + SAMPLE="$sample_dir/$(basename "$SAMPLE")" +fi # Presets: reduce "AI refuses to work" friction for legitimate local/CTF work. case "$PRESET" in @@ -104,7 +123,7 @@ if [[ -n "$NETWORK_PROFILE" ]]; then esac fi -CASE_ROOT="$PACKAGE_ROOT/work/$CASE_NAME" +CASE_ROOT="$PROJECT_ROOT/work/$CASE_NAME" mkdir -p "$CASE_ROOT/evidence" "$CASE_ROOT/notes" "$CASE_ROOT/report" auth_status_resolved="pending" @@ -165,7 +184,7 @@ primary="reverse-engineering/SKILL.md" primary_id="R0" if [[ -f "$SCRIPT_DIR/master-route.sh" ]]; then set +e - bash "$SCRIPT_DIR/master-route.sh" --hint "$HINT" --out-dir "$ROUTE_TMP" >/dev/null 2>&1 + bash "$SCRIPT_DIR/master-route.sh" --hint "$HINT" --project-root "$PROJECT_ROOT" --out-dir "$ROUTE_TMP" >/dev/null 2>&1 set -e if [[ -f "$ROUTE_TMP/route-scope.md" ]]; then rt="$(cat "$ROUTE_TMP/route-scope.md")" @@ -215,6 +234,7 @@ cat > "$CASE_ROOT/scope.md" <&1 | Out-Null if ($LASTEXITCODE -eq 0) { Ok 'case-guard ready exit 0' } else { Bad "case-guard ready exit $LASTEXITCODE" } @@ -156,7 +156,7 @@ $bareRoot = Join-Path $PackageRoot ("work\{0}" -f $bareName) & $HostExe -NoProfile -ExecutionPolicy Bypass -File $cg -CaseRoot $bareRoot 2>&1 | Out-Null if ($LASTEXITCODE -eq 2) { Ok 'case-guard pending exit 2' } else { Bad "case-guard pending expected 2 got $LASTEXITCODE" } & $HostExe -NoProfile -ExecutionPolicy Bypass -File $cg -CaseRoot $bareRoot -Force 2>&1 | Out-Null -if ($LASTEXITCODE -eq 0) { Ok 'case-guard -Force exit 0' } else { Bad "case-guard -Force exit $LASTEXITCODE" } +if ($LASTEXITCODE -eq 2) { Ok 'case-guard -Force cannot bypass hard gate' } else { Bad "case-guard -Force expected 2 got $LASTEXITCODE" } # 10) AuthGranted must not be clobbered by junk AuthStatus / multi-asset lab init # Note: -ProjectRoot is passed explicitly to prevent the -InScopeAssets array