diff --git a/deploy/README.md b/deploy/README.md index 2ac6a45..d3c1ba0 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -265,7 +265,8 @@ The only artifact paths exposed are the text-oriented report bundle files: - `report_bundle/report/report.json` - `report_bundle/report/run_summary.json` - `report_bundle/poc/PoC.t.sol` -- `report_bundle/poc/LumosPoCBase.sol` +- `report_bundle/poc/Base.sol` (current PoC support contract) +- `report_bundle/poc/LumosPoCBase.sol` (legacy compatibility) - `report_bundle/evidence/asset_deltas.json` - `report_bundle/evidence/fund_flows.json` - `report_bundle/visuals/asset_deltas.dot` @@ -310,8 +311,10 @@ intentionally mount the output directory read-only to the MCP runtime. ## ECW internal export Backlight can expose a dedicated ECW bundle without changing the operator API, -MCP tool list, worker queue, or the product artifact allowlist. The endpoint is -disabled unless a separate token is configured: +MCP tool list, or worker queue. The ECW endpoint uses a broader allowlist than +the narrow product artifact profile, which includes the current and legacy PoC +support contracts required by report bundles. The endpoint is disabled unless +a separate token is configured: ```bash HELIOS_ECW_EXPORT_TOKEN= diff --git a/docs/results.md b/docs/results.md index 85b0ca3..9912e3b 100644 --- a/docs/results.md +++ b/docs/results.md @@ -128,7 +128,8 @@ Common artifact paths: - `report_bundle/report/report.json` - `report_bundle/report/run_summary.json` - `report_bundle/poc/PoC.t.sol` -- `report_bundle/poc/LumosPoCBase.sol` +- `report_bundle/poc/Base.sol` (current PoC support contract) +- `report_bundle/poc/LumosPoCBase.sol` (legacy compatibility) - `report_bundle/evidence/asset_deltas.json` - `report_bundle/evidence/fund_flows.json` - `report_bundle/visuals/asset_deltas.dot` diff --git a/internal/artifacts/ecw_reader_test.go b/internal/artifacts/ecw_reader_test.go index dd7b4ff..28361c2 100644 --- a/internal/artifacts/ecw_reader_test.go +++ b/internal/artifacts/ecw_reader_test.go @@ -69,6 +69,37 @@ func TestECWReaderExposesDedicatedProfileWithoutChangingDefault(t *testing.T) { } } +// TestECWReaderServesRenamedPoCBaseContract guards the self-containment of the +// exported PoC project. The engine renamed the PoC base contract file from +// LumosPoCBase.sol to Base.sol (PoC.t.sol imports "./Base.sol"); the export +// allowlist must expose Base.sol at both PoC locations so the exported bundle +// compiles standalone instead of dropping the base file the PoC imports. +func TestECWReaderServesRenamedPoCBaseContract(t *testing.T) { + base := t.TempDir() + root := filepath.Join(base, "case_1") + writeArtifactFile(t, root, "report_bundle/poc/PoC.t.sol", "import \"./Base.sol\";\ncontract AttackTest is Base {}\n") + writeArtifactFile(t, root, "report_bundle/poc/Base.sol", "abstract contract Base {}\n") + writeArtifactFile(t, root, "artifacts/agent_poc/foundry/test/PoC.t.sol", "import \"./Base.sol\";\ncontract AttackTest is Base {}\n") + writeArtifactFile(t, root, "artifacts/agent_poc/foundry/test/Base.sol", "abstract contract Base {}\n") + + reader, err := NewECWReader(base, 0) + if err != nil { + t.Fatal(err) + } + for _, rel := range []string{"report_bundle/poc/Base.sol", "artifacts/agent_poc/foundry/test/Base.sol"} { + if !slices.Contains(reader.AllowedPaths(), rel) { + t.Fatalf("ECW export profile missing renamed PoC base %q; exported PoC would not build", rel) + } + result, err := reader.Read(caseWithRoot(root), rel, 0) + if err != nil { + t.Fatalf("ECW Read(%q): %v", rel, err) + } + if result.Text != "abstract contract Base {}\n" { + t.Fatalf("unexpected PoC base content for %q: %+v", rel, result) + } + } +} + func writeArtifactFile(t *testing.T, root, rel, body string) { t.Helper() path := filepath.Join(root, rel) diff --git a/internal/artifacts/reader.go b/internal/artifacts/reader.go index 2258ac0..6cf6370 100644 --- a/internal/artifacts/reader.go +++ b/internal/artifacts/reader.go @@ -33,6 +33,7 @@ var defaultArtifacts = []artifactSpec{ {PublicPath: "report_bundle/report/report.json", SourcePath: "report_bundle/report/report.json"}, {PublicPath: "report_bundle/report/run_summary.json", SourcePath: "report_bundle/report/run_summary.json"}, {PublicPath: "report_bundle/poc/PoC.t.sol", SourcePath: "report_bundle/poc/PoC.t.sol"}, + {PublicPath: "report_bundle/poc/Base.sol", SourcePath: "report_bundle/poc/Base.sol"}, {PublicPath: "report_bundle/poc/LumosPoCBase.sol", SourcePath: "report_bundle/poc/LumosPoCBase.sol"}, {PublicPath: "report_bundle/evidence/asset_deltas.json", SourcePath: "report_bundle/evidence/asset_deltas.json"}, {PublicPath: "report_bundle/evidence/fund_flows.json", SourcePath: "report_bundle/evidence/fund_flows.json"}, @@ -62,6 +63,7 @@ var ecwArtifacts = []artifactSpec{ {PublicPath: "report_bundle/report/report.json", SourcePath: "report_bundle/report/report.json"}, {PublicPath: "report_bundle/report/run_summary.json", SourcePath: "report_bundle/report/run_summary.json"}, {PublicPath: "report_bundle/poc/PoC.t.sol", SourcePath: "report_bundle/poc/PoC.t.sol"}, + {PublicPath: "report_bundle/poc/Base.sol", SourcePath: "report_bundle/poc/Base.sol"}, {PublicPath: "report_bundle/poc/LumosPoCBase.sol", SourcePath: "report_bundle/poc/LumosPoCBase.sol"}, {PublicPath: "report_bundle/evidence/asset_deltas.json", SourcePath: "report_bundle/evidence/asset_deltas.json"}, {PublicPath: "report_bundle/evidence/fund_flows.json", SourcePath: "report_bundle/evidence/fund_flows.json"}, @@ -82,6 +84,7 @@ var ecwArtifacts = []artifactSpec{ {PublicPath: "artifacts/agent_poc/summary.json", SourcePath: "artifacts/agent_poc/summary.json"}, {PublicPath: "artifacts/agent_poc/foundry/foundry.toml", SourcePath: "artifacts/agent_poc/foundry/foundry.toml"}, {PublicPath: "artifacts/agent_poc/foundry/test/PoC.t.sol", SourcePath: "artifacts/agent_poc/foundry/test/PoC.t.sol"}, + {PublicPath: "artifacts/agent_poc/foundry/test/Base.sol", SourcePath: "artifacts/agent_poc/foundry/test/Base.sol"}, {PublicPath: "artifacts/agent_poc/foundry/test/LumosPoCBase.sol", SourcePath: "artifacts/agent_poc/foundry/test/LumosPoCBase.sol"}, {PublicPath: "artifacts/agent_poc/foundry/lib/forge-std/src/Test.sol", SourcePath: "artifacts/agent_poc/foundry/lib/forge-std/src/Test.sol"}, {PublicPath: "artifacts/poc_sketch/poc_sketch.sol", SourcePath: "artifacts/poc_sketch/poc_sketch.sol"}, diff --git a/internal/artifacts/reader_test.go b/internal/artifacts/reader_test.go index e361a5c..9ee477d 100644 --- a/internal/artifacts/reader_test.go +++ b/internal/artifacts/reader_test.go @@ -40,6 +40,9 @@ func TestReaderExposesPrettyProductArtifactAliases(t *testing.T) { if err := os.WriteFile(filepath.Join(root, "report_bundle", "poc", "LumosPoCBase.sol"), []byte("abstract contract LumosPoCBase {}\n"), 0o644); err != nil { t.Fatal(err) } + if err := os.WriteFile(filepath.Join(root, "report_bundle", "poc", "Base.sol"), []byte("abstract contract Base {}\n"), 0o644); err != nil { + t.Fatal(err) + } if err := os.WriteFile(filepath.Join(root, "report_bundle", "evidence", "asset_deltas.json"), []byte(`[]`), 0o644); err != nil { t.Fatal(err) } @@ -151,6 +154,13 @@ func TestReaderExposesPrettyProductArtifactAliases(t *testing.T) { if pocBase.Text != "abstract contract LumosPoCBase {}\n" { t.Fatalf("unexpected PoC base: %+v", pocBase) } + currentPoCBase, err := reader.Read(c, "report_bundle/poc/Base.sol", 0) + if err != nil { + t.Fatal(err) + } + if currentPoCBase.Text != "abstract contract Base {}\n" { + t.Fatalf("unexpected current PoC base: %+v", currentPoCBase) + } for _, path := range []string{"summary.json", "summary.md", "rca.md", "Report.md", "report_bundle/visuals/asset_deltas.png", "artifacts/secret.json", "../RCA.md", "/tmp/RCA.md"} { if _, err := reader.Read(c, path, 0); err == nil {