[pitboss/grind] deferred session-0011 (20260522T163126Z-7d60)

This commit is contained in:
pitboss 2026-05-22 16:11:50 -05:00
parent 089fe3556a
commit 6c4322832f
3 changed files with 606 additions and 6 deletions

View file

@ -603,6 +603,32 @@ pub fn emit(spec: &HarnessSpec) -> Result<HarnessSource, UnsupportedReason> {
return Ok(emit_json_parse_harness(spec));
}
// Phase 11 (Track J.9): UNAUTHORIZED_ID IDOR harness. Imports the
// fixture under `internal/vulnentry`, invokes
// `vulnentry.<EntryFn>(payload)`, and emits a
// `ProbeKind::IdorAccess { caller_id: "alice", owner_id: payload }`
// probe whenever the fixture materialises a present record. A
// `reflect`-driven presence check (`string != ""`, non-`nil` for
// pointer / slice / map / interface, non-zero struct) covers the
// current `func Run(string) string` fixture shape and stays correct
// under future return-type variations.
if spec.expected_cap == crate::labels::Cap::UNAUTHORIZED_ID {
return Ok(emit_unauthorized_id_harness(spec));
}
// Phase 11 (Track J.9): DATA_EXFIL outbound-network harness. Go has
// no monkey-patch hook for `http.Get` / `http.Post`, but
// `http.DefaultTransport` is a public `RoundTripper`-typed variable
// — replacing it before the fixture runs intercepts every default-
// client request before any wire I/O. The harness's
// `nyxRoundTripper` parses the request URL host, emits a
// `ProbeKind::OutboundNetwork { host }` probe, and returns a benign
// empty 200 OK response so the fixture's discarded result is
// satisfied without a real connection.
if spec.expected_cap == crate::labels::Cap::DATA_EXFIL {
return Ok(emit_data_exfil_harness(spec));
}
// ClassMethod short-circuit. Go has no
// classes — the dispatcher treats `class` as a top-level struct
// declared in the entry file and `method` as a method on its
@ -1570,6 +1596,250 @@ func nyxJsonParseProbe(depth int, excessive bool) {{
}
}
/// Phase 11 (Track J.9) UNAUTHORIZED_ID IDOR harness for Go.
///
/// Imports the fixture under `internal/vulnentry`, invokes
/// `vulnentry.<EntryFn>(payload)`, and emits a
/// [`crate::dynamic::probe::ProbeKind::IdorAccess`] probe iff the
/// fixture materialises a present record. Presence is decided via
/// `reflect`: `string != ""`, non-`nil` for pointer / slice / map /
/// interface / channel / func, non-zero for struct. The
/// `IdorBoundaryCrossed` predicate fires when `caller_id != owner_id`;
/// the harness pins `caller_id = "alice"` and treats the payload as
/// `owner_id`. Falls back to a payload-only path that emits an
/// `IdorAccess(alice, payload)` probe when the fixture source is
/// unreachable so the universal sink-hit path still fires.
pub fn emit_unauthorized_id_harness(spec: &HarnessSpec) -> HarnessSource {
let shim = probe_shim();
let go_mod = generate_go_mod();
let entry_fn = capitalize_first(&spec.entry_name);
let entry_source = read_entry_source(&spec.entry_file);
let mut extra_files = vec![("go.mod".to_owned(), go_mod)];
let tier_a_active = !entry_source.is_empty();
let (extra_imports, via_fixture_decl, via_fixture_invoke) = if tier_a_active {
let rewritten = rewrite_package(&entry_source, "vulnentry");
extra_files.push(("internal/vulnentry/vulnentry.go".to_owned(), rewritten));
let decl = format!(
r##"func nyxRecordPresent(v reflect.Value) bool {{
if !v.IsValid() {{
return false
}}
switch v.Kind() {{
case reflect.String:
return v.String() != ""
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface, reflect.Chan, reflect.Func:
return !v.IsNil()
case reflect.Struct:
return !v.IsZero()
default:
return !v.IsZero()
}}
}}
func nyxUnauthorizedIdViaFixture(payload string) bool {{
defer func() {{ _ = recover() }}()
produced := vulnentry.{entry_fn}(payload)
return nyxRecordPresent(reflect.ValueOf(produced))
}}
"##
);
let invoke = "\tif nyxUnauthorizedIdViaFixture(payload) {\n\t\tnyxIdorAccessProbe(_NYX_CALLER_ID, payload)\n\t}\n".to_owned();
(
"\t\"reflect\"\n\n\t\"nyx-harness/internal/vulnentry\"\n",
decl,
invoke,
)
} else {
(
"",
String::new(),
"\tnyxIdorAccessProbe(_NYX_CALLER_ID, payload)\n".to_owned(),
)
};
let source = format!(
r##"// Nyx dynamic harness — UNAUTHORIZED_ID IDOR boundary (Phase 11 / Track J.9).
package main
import (
"encoding/json"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
{extra_imports})
{shim}
const _NYX_CALLER_ID = "alice"
func nyxIdorAccessProbe(caller, owner string) {{
__nyx_emit(map[string]interface{{}}{{
"sink_callee": "__nyx_idor_lookup",
"args": []map[string]interface{{}}{{
{{"kind": "String", "value": caller}},
{{"kind": "String", "value": owner}},
}},
"captured_at_ns": uint64(time.Now().UnixNano()),
"payload_id": os.Getenv("NYX_PAYLOAD_ID"),
"kind": map[string]interface{{}}{{
"kind": "IdorAccess",
"caller_id": caller,
"owner_id": owner,
}},
"witness": __nyx_witness("__nyx_idor_lookup", []string{{caller, owner}}),
}})
}}
{via_fixture_decl}func main() {{
__nyx_install_crash_guard("__nyx_idor_lookup")
defer __nyx_recover_crash("__nyx_idor_lookup")()
payload := os.Getenv("NYX_PAYLOAD")
{via_fixture_invoke} fmt.Println("__NYX_SINK_HIT__")
body, _ := json.Marshal(map[string]interface{{}}{{"payload_len": len(payload)}})
fmt.Println(string(body))
}}
"##
);
HarnessSource {
source,
filename: "main.go".to_owned(),
command: vec!["./nyx_harness".to_owned()],
extra_files,
entry_subpath: Some("entry/entry.go".to_owned()),
}
}
/// Phase 11 (Track J.9) DATA_EXFIL outbound-network harness for Go.
///
/// Imports the fixture under `internal/vulnentry`, replaces
/// `http.DefaultTransport` and `http.DefaultClient.Transport` with a
/// `nyxRoundTripper` that captures the request URL host before any
/// wire I/O, emits a
/// [`crate::dynamic::probe::ProbeKind::OutboundNetwork`] probe, and
/// returns a benign empty 200 OK response so the fixture's discarded
/// result is satisfied without a real connection. `http.Get` /
/// `http.Post` / `http.Client.Do` all route through `Client.transport()`
/// which falls back to `DefaultTransport` when `Client.Transport` is
/// `nil`, so the override covers the package-level helpers as well as
/// any fixture-built `&http.Client{}` whose `Transport` field stays
/// default. The
/// [`crate::dynamic::oracle::ProbePredicate::OutboundHostNotIn`]
/// predicate fires when the captured host falls outside the loopback
/// allowlist. Falls back to a payload-only path that emits an
/// `OutboundNetwork(payload)` probe when the fixture source is
/// unreachable so the universal sink-hit path still fires.
pub fn emit_data_exfil_harness(spec: &HarnessSpec) -> HarnessSource {
let shim = probe_shim();
let go_mod = generate_go_mod();
let entry_fn = capitalize_first(&spec.entry_name);
let entry_source = read_entry_source(&spec.entry_file);
let mut extra_files = vec![("go.mod".to_owned(), go_mod)];
let tier_a_active = !entry_source.is_empty();
let (extra_imports, via_fixture_decl, via_fixture_invoke) = if tier_a_active {
let rewritten = rewrite_package(&entry_source, "vulnentry");
extra_files.push(("internal/vulnentry/vulnentry.go".to_owned(), rewritten));
let decl = r##"type nyxRoundTripper struct{}
func (nyxRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
host := ""
if req != nil && req.URL != nil {
host = req.URL.Hostname()
if host == "" {
host = req.URL.Host
}
}
if host != "" {
nyxOutboundProbe(host)
}
return &http.Response{
Status: "200 OK",
StatusCode: 200,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: make(http.Header),
Body: io.NopCloser(bytes.NewReader(nil)),
Request: req,
}, nil
}
func nyxInstallHttpTransport() {
rt := nyxRoundTripper{}
http.DefaultTransport = rt
http.DefaultClient = &http.Client{Transport: rt}
}
func nyxDataExfilViaFixture(payload string) {
defer func() { _ = recover() }()
vulnentry."##.to_owned()
+ &format!("{entry_fn}(payload)\n}}\n\n");
let invoke =
"\tnyxInstallHttpTransport()\n\tnyxDataExfilViaFixture(payload)\n".to_owned();
(
"\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"nyx-harness/internal/vulnentry\"\n",
decl,
invoke,
)
} else {
(
"",
String::new(),
"\tnyxOutboundProbe(payload)\n".to_owned(),
)
};
let source = format!(
r##"// Nyx dynamic harness — DATA_EXFIL outbound-host (Phase 11 / Track J.9).
package main
import (
"encoding/json"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
{extra_imports})
{shim}
func nyxOutboundProbe(host string) {{
__nyx_emit(map[string]interface{{}}{{
"sink_callee": "__nyx_mock_http",
"args": []map[string]interface{{}}{{
{{"kind": "String", "value": host}},
}},
"captured_at_ns": uint64(time.Now().UnixNano()),
"payload_id": os.Getenv("NYX_PAYLOAD_ID"),
"kind": map[string]interface{{}}{{"kind": "OutboundNetwork", "host": host}},
"witness": __nyx_witness("__nyx_mock_http", []string{{host}}),
}})
}}
{via_fixture_decl}func main() {{
__nyx_install_crash_guard("__nyx_mock_http")
defer __nyx_recover_crash("__nyx_mock_http")()
payload := os.Getenv("NYX_PAYLOAD")
{via_fixture_invoke} fmt.Println("__NYX_SINK_HIT__")
body, _ := json.Marshal(map[string]interface{{}}{{"payload_len": len(payload)}})
fmt.Println(string(body))
}}
"##
);
HarnessSource {
source,
filename: "main.go".to_owned(),
command: vec!["./nyx_harness".to_owned()],
extra_files,
entry_subpath: Some("entry/entry.go".to_owned()),
}
}
/// Phase 19 (Track M.1) — class-method harness for Go.
///
/// `class` is mapped to a struct type declared in `entry/entry.go`
@ -2861,4 +3131,247 @@ mod tests {
"fallback path must still emit a JSON_PARSE probe so the universal sink-hit path fires",
);
}
// ── Phase 11 (Track J.9) Go UNAUTHORIZED_ID emitter tests ──────────────────
fn make_unauthorized_id_spec(entry_file: &str, entry_name: &str) -> HarnessSpec {
let mut spec = make_spec(PayloadSlot::Param(0));
spec.expected_cap = Cap::UNAUTHORIZED_ID;
spec.entry_file = entry_file.to_owned();
spec.entry_name = entry_name.to_owned();
spec
}
#[test]
fn emit_dispatches_to_unauthorized_id_harness_when_cap_is_unauthorized_id() {
let h = emit(&make_unauthorized_id_spec(
"tests/dynamic_fixtures/unauthorized_id/go/vuln.go",
"Run",
))
.unwrap();
assert!(
h.source.contains("nyxIdorAccessProbe"),
"dispatcher must short-circuit Cap::UNAUTHORIZED_ID into emit_unauthorized_id_harness so the IDOR probe shim is present",
);
assert!(
h.source.contains("\"kind\": \"IdorAccess\""),
"Go UNAUTHORIZED_ID harness must record probes with kind IdorAccess so IdorBoundaryCrossed fires",
);
}
#[test]
fn emit_unauthorized_id_harness_pins_caller_id() {
let h = emit_unauthorized_id_harness(&make_unauthorized_id_spec(
"tests/dynamic_fixtures/unauthorized_id/go/vuln.go",
"Run",
));
assert!(
h.source.contains("const _NYX_CALLER_ID = \"alice\""),
"Go UNAUTHORIZED_ID harness must pin caller_id to \"alice\"",
);
assert!(
h.source.contains("nyxIdorAccessProbe(_NYX_CALLER_ID, payload)"),
"Go UNAUTHORIZED_ID harness must call probe with caller_id + payload-as-owner",
);
}
#[test]
fn emit_unauthorized_id_harness_gates_probe_on_record_presence() {
let h = emit_unauthorized_id_harness(&make_unauthorized_id_spec(
"tests/dynamic_fixtures/unauthorized_id/go/benign.go",
"Run",
));
assert!(
h.source.contains("if nyxUnauthorizedIdViaFixture(payload) {"),
"Go UNAUTHORIZED_ID harness must gate probe emission on a present record so the benign fixture's empty-string rejection clears the predicate",
);
assert!(
h.source.contains("func nyxRecordPresent("),
"Go UNAUTHORIZED_ID harness must define a reflect-driven presence check that handles string / pointer / map / interface returns",
);
}
#[test]
fn emit_unauthorized_id_harness_routes_through_internal_vulnentry_package() {
let h = emit_unauthorized_id_harness(&make_unauthorized_id_spec(
"tests/dynamic_fixtures/unauthorized_id/go/vuln.go",
"Run",
));
let staged = h
.extra_files
.iter()
.find(|(name, _)| name == "internal/vulnentry/vulnentry.go");
assert!(
staged.is_some(),
"tier-(a) UNAUTHORIZED_ID harness must stage the fixture under internal/vulnentry/ so main.go can import it",
);
let body = &staged.unwrap().1;
assert!(
body.contains("package vulnentry"),
"fixture package name must be rewritten to vulnentry so the import path resolves",
);
assert!(
h.source.contains("nyx-harness/internal/vulnentry"),
"main.go must import the rewritten vulnentry package",
);
assert!(
h.source.contains("vulnentry.Run(payload)"),
"main.go must invoke the entry function on the rewritten fixture",
);
}
#[test]
fn emit_unauthorized_id_harness_falls_back_when_fixture_source_unavailable() {
let mut spec = make_spec(PayloadSlot::Param(0));
spec.expected_cap = Cap::UNAUTHORIZED_ID;
spec.entry_file = "/nonexistent/path/missing.go".into();
spec.entry_name = "Run".into();
let h = emit_unauthorized_id_harness(&spec);
let staged = h
.extra_files
.iter()
.find(|(name, _)| name == "internal/vulnentry/vulnentry.go");
assert!(
staged.is_none(),
"fallback path must not stage a vulnentry copy when the fixture cannot be read",
);
assert!(
h.source.contains("nyxIdorAccessProbe(_NYX_CALLER_ID, payload)"),
"fallback path must still emit an IDOR probe so the universal sink-hit path fires",
);
}
// ── Phase 11 (Track J.9) Go DATA_EXFIL emitter tests ───────────────────────
fn make_data_exfil_spec(entry_file: &str, entry_name: &str) -> HarnessSpec {
let mut spec = make_spec(PayloadSlot::Param(0));
spec.expected_cap = Cap::DATA_EXFIL;
spec.entry_file = entry_file.to_owned();
spec.entry_name = entry_name.to_owned();
spec
}
#[test]
fn emit_dispatches_to_data_exfil_harness_when_cap_is_data_exfil() {
let h = emit(&make_data_exfil_spec(
"tests/dynamic_fixtures/data_exfil/go/vuln.go",
"Run",
))
.unwrap();
assert!(
h.source.contains("nyxOutboundProbe"),
"dispatcher must short-circuit Cap::DATA_EXFIL into emit_data_exfil_harness so the outbound probe shim is present",
);
assert!(
h.source.contains("\"kind\": \"OutboundNetwork\""),
"Go DATA_EXFIL harness must record probes with kind OutboundNetwork so OutboundHostNotIn fires",
);
}
#[test]
fn emit_data_exfil_harness_overrides_default_transport() {
let h = emit_data_exfil_harness(&make_data_exfil_spec(
"tests/dynamic_fixtures/data_exfil/go/vuln.go",
"Run",
));
assert!(
h.source.contains("type nyxRoundTripper struct{}"),
"Go DATA_EXFIL harness must define the nyxRoundTripper interceptor type",
);
assert!(
h.source.contains("http.DefaultTransport = rt"),
"Go DATA_EXFIL harness must override http.DefaultTransport so package-level http.Get routes through the interceptor",
);
assert!(
h.source
.contains("http.DefaultClient = &http.Client{Transport: rt}"),
"Go DATA_EXFIL harness must override http.DefaultClient so consumers that call DefaultClient.Do also route through the interceptor",
);
}
#[test]
fn emit_data_exfil_harness_parses_host_via_url_hostname() {
let h = emit_data_exfil_harness(&make_data_exfil_spec(
"tests/dynamic_fixtures/data_exfil/go/vuln.go",
"Run",
));
assert!(
h.source.contains("req.URL.Hostname()"),
"Go DATA_EXFIL harness must extract host via req.URL.Hostname()",
);
assert!(
h.source.contains("nyxOutboundProbe(host)"),
"Go DATA_EXFIL harness must emit the outbound probe with the parsed host",
);
}
#[test]
fn emit_data_exfil_harness_installs_transport_before_fixture_call() {
let h = emit_data_exfil_harness(&make_data_exfil_spec(
"tests/dynamic_fixtures/data_exfil/go/vuln.go",
"Run",
));
let install_idx = h
.source
.find("nyxInstallHttpTransport()")
.expect("install call present");
let fixture_idx = h
.source
.find("nyxDataExfilViaFixture(payload)")
.expect("fixture call present");
assert!(
install_idx < fixture_idx,
"Go DATA_EXFIL harness must install the transport override before invoking the fixture so the first http.Get is intercepted",
);
}
#[test]
fn emit_data_exfil_harness_routes_through_internal_vulnentry_package() {
let h = emit_data_exfil_harness(&make_data_exfil_spec(
"tests/dynamic_fixtures/data_exfil/go/vuln.go",
"Run",
));
let staged = h
.extra_files
.iter()
.find(|(name, _)| name == "internal/vulnentry/vulnentry.go");
assert!(
staged.is_some(),
"tier-(a) DATA_EXFIL harness must stage the fixture under internal/vulnentry/ so main.go can import it",
);
let body = &staged.unwrap().1;
assert!(
body.contains("package vulnentry"),
"fixture package name must be rewritten to vulnentry so the import path resolves",
);
assert!(
h.source.contains("nyx-harness/internal/vulnentry"),
"main.go must import the rewritten vulnentry package",
);
assert!(
h.source.contains("vulnentry.Run(payload)"),
"main.go must invoke the entry function on the rewritten fixture",
);
}
#[test]
fn emit_data_exfil_harness_falls_back_when_fixture_source_unavailable() {
let mut spec = make_spec(PayloadSlot::Param(0));
spec.expected_cap = Cap::DATA_EXFIL;
spec.entry_file = "/nonexistent/path/missing.go".into();
spec.entry_name = "Run".into();
let h = emit_data_exfil_harness(&spec);
let staged = h
.extra_files
.iter()
.find(|(name, _)| name == "internal/vulnentry/vulnentry.go");
assert!(
staged.is_none(),
"fallback path must not stage a vulnentry copy when the fixture cannot be read",
);
assert!(
h.source.contains("nyxOutboundProbe(payload)"),
"fallback path must still emit an outbound probe so the universal sink-hit path fires",
);
}
}

View file

@ -134,8 +134,13 @@ mod e2e_data_exfil {
use tempfile::TempDir;
fn command_available(bin: &str) -> bool {
// Go's CLI uses `go version` (subcommand) instead of `go
// --version` and exits non-zero on `--version`. Every other
// toolchain here (python3, ruby, node, javac, php) accepts
// `--version`.
let arg = if bin == "go" { "version" } else { "--version" };
Command::new(bin)
.arg("--version")
.arg(arg)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
@ -150,8 +155,9 @@ mod e2e_data_exfil {
Lang::JavaScript => "js",
Lang::Java => "java",
Lang::Php => "php",
Lang::Go => "go",
_ => unreachable!(
"DATA_EXFIL e2e currently covers Python + Ruby + JavaScript + Java + Php"
"DATA_EXFIL e2e currently covers Python + Ruby + JavaScript + Java + Php + Go"
),
})
.join(fixture);
@ -197,8 +203,9 @@ mod e2e_data_exfil {
Lang::JavaScript => "node",
Lang::Java => "javac",
Lang::Php => "php",
Lang::Go => "go",
_ => unreachable!(
"DATA_EXFIL e2e currently covers Python + Ruby + JavaScript + Java + Php"
"DATA_EXFIL e2e currently covers Python + Ruby + JavaScript + Java + Php + Go"
),
};
if !command_available(required) {
@ -404,4 +411,41 @@ mod e2e_data_exfil {
"PHP DATA_EXFIL benign control must not confirm via run_spec; got {outcome:?}",
);
}
/// Go pair, same shape as Python + Ruby + JavaScript + Java + Php.
/// The vuln fixture calls `http.Get("http://" + host + "/exfil?...")`;
/// the harness replaces `http.DefaultTransport` with a custom
/// `RoundTripper` that captures `req.URL.Hostname()` before any
/// wire I/O, emits a `ProbeKind::OutboundNetwork`, and returns a
/// benign empty 200 response. `OutboundHostNotIn` fires for the
/// `attacker.test` payload. The benign fixture's
/// `if _, ok := allowlist[host]; !ok { return }` guard short-
/// circuits before `http.Get` for non-loopback payloads so no
/// probe fires. Skips when `go` is not on PATH.
#[test]
fn go_vuln_confirms_via_run_spec() {
let Some(outcome) = run(Lang::Go, "vuln.go", "Run") else {
return;
};
assert!(
outcome.triggered_by.is_some(),
"Go DATA_EXFIL vuln must confirm via run_spec; got {outcome:?}",
);
let diff = outcome
.differential
.as_ref()
.expect("confirmed run must carry a DifferentialOutcome");
assert_eq!(diff.verdict, DifferentialVerdict::Confirmed);
}
#[test]
fn go_benign_does_not_confirm_via_run_spec() {
let Some(outcome) = run(Lang::Go, "benign.go", "Run") else {
return;
};
assert!(
outcome.triggered_by.is_none(),
"Go DATA_EXFIL benign control must not confirm via run_spec; got {outcome:?}",
);
}
}

View file

@ -125,8 +125,13 @@ mod e2e_unauthorized_id {
use tempfile::TempDir;
fn command_available(bin: &str) -> bool {
// Go's CLI uses `go version` (subcommand) instead of `go
// --version` and exits non-zero on `--version`. Every other
// toolchain here (python3, ruby, node, javac, php) accepts
// `--version`.
let arg = if bin == "go" { "version" } else { "--version" };
Command::new(bin)
.arg("--version")
.arg(arg)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
@ -141,8 +146,9 @@ mod e2e_unauthorized_id {
Lang::JavaScript => "js",
Lang::Java => "java",
Lang::Php => "php",
Lang::Go => "go",
_ => unreachable!(
"UNAUTHORIZED_ID e2e currently covers Python + Ruby + JavaScript + Java + Php"
"UNAUTHORIZED_ID e2e currently covers Python + Ruby + JavaScript + Java + Php + Go"
),
})
.join(fixture);
@ -188,8 +194,9 @@ mod e2e_unauthorized_id {
Lang::JavaScript => "node",
Lang::Java => "javac",
Lang::Php => "php",
Lang::Go => "go",
_ => unreachable!(
"UNAUTHORIZED_ID e2e currently covers Python + Ruby + JavaScript + Java + Php"
"UNAUTHORIZED_ID e2e currently covers Python + Ruby + JavaScript + Java + Php + Go"
),
};
if !command_available(required) {
@ -387,4 +394,40 @@ mod e2e_unauthorized_id {
"PHP UNAUTHORIZED_ID benign control must not confirm via run_spec; got {outcome:?}",
);
}
/// Go pair, same shape as Python + Ruby + JavaScript + Java + Php.
/// The vuln fixture's `store[ownerID]` materialises `"bob@x"` for
/// the `bob` payload; the harness's `reflect`-driven presence check
/// fires the `IdorAccess(alice, bob)` probe and
/// `IdorBoundaryCrossed` confirms the differential. The benign
/// fixture's `if ownerID != callerID { return "" }` short-circuit
/// returns an empty string for the non-caller payload so the
/// presence check clears and no probe fires. Skips when `go` is
/// not on PATH.
#[test]
fn go_vuln_confirms_via_run_spec() {
let Some(outcome) = run(Lang::Go, "vuln.go", "Run") else {
return;
};
assert!(
outcome.triggered_by.is_some(),
"Go UNAUTHORIZED_ID vuln must confirm via run_spec; got {outcome:?}",
);
let diff = outcome
.differential
.as_ref()
.expect("confirmed run must carry a DifferentialOutcome");
assert_eq!(diff.verdict, DifferentialVerdict::Confirmed);
}
#[test]
fn go_benign_does_not_confirm_via_run_spec() {
let Some(outcome) = run(Lang::Go, "benign.go", "Run") else {
return;
};
assert!(
outcome.triggered_by.is_none(),
"Go UNAUTHORIZED_ID benign control must not confirm via run_spec; got {outcome:?}",
);
}
}