[pitboss/grind] deferred session-0011 (20260516T052512Z-20f8)

This commit is contained in:
pitboss 2026-05-16 06:54:45 -05:00
parent c162c638a2
commit d126f3c15c
15 changed files with 510 additions and 10 deletions

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 13

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 17

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 14

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 15

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 16

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 18

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 12

View file

@ -121,6 +121,26 @@ def __nyx_install_crash_guard(sink_callee):
except (OSError, ValueError):
pass
# Phase 10 (Track D.3) stub helpers. When the verifier spawned a SqlStub it
# publishes the queries-log path through NYX_SQL_LOG; a sink call site that
# wants the host-side stub to see its query appends one record-per-call. The
# helper is a no-op when NYX_SQL_LOG is unset so the same fixture source still
# runs under harness modes that didn't spawn a stub.
def __nyx_stub_sql_record(query, **detail):
import os
p = os.environ.get("NYX_SQL_LOG")
if not p:
return
try:
with open(p, "a") as _f:
for k, v in detail.items():
_f.write('# %s: %s\n' % (str(k), str(v)))
_f.write(str(query))
if not str(query).endswith('\n'):
_f.write('\n')
except OSError:
pass
_NYX_SINK_FILE = "<TMPDIR>/<ENTRY_FILE>"
_NYX_SINK_LINE = 14

View file

@ -0,0 +1,39 @@
"""Phase 10 (Track D.3) stub-end-to-end fixture: Python + SQL.
The verifier publishes:
* ``NYX_SQL_ENDPOINT`` absolute path of a SQLite DB the SqlStub owns.
* ``NYX_SQL_LOG`` companion log path the harness appends executed
queries to so the host SqlStub picks them up on ``drain_events()``.
This fixture exercises both: it opens the stub DB with stdlib ``sqlite3``,
runs a tautology SELECT (``OR 1=1``), and forwards the executed query to
the stub through the Python shim helper ``__nyx_stub_sql_record``. The
companion test in ``tests/stubs_e2e_per_lang.rs`` splices in
``crate::dynamic::lang::python::probe_shim`` ahead of this source, runs it
with both env vars set, and asserts the stub captured the tautology.
"""
import os
import sqlite3
def main():
db_path = os.environ.get("NYX_SQL_ENDPOINT")
if not db_path:
return
query = "SELECT 1 WHERE 'a' = 'a' OR 1=1 --"
conn = sqlite3.connect(db_path)
try:
rows = conn.execute(query).fetchall()
for row in rows:
print(row[0])
finally:
conn.close()
# Record the executed query through the probe shim so the host
# SqlStub captures it on the next drain_events() call.
__nyx_stub_sql_record(query, driver="sqlite3")
if __name__ == "__main__":
main()

144
tests/stubs_e2e_per_lang.rs Normal file
View file

@ -0,0 +1,144 @@
//! Phase 10 (Track D.3) — per-(lang, cap) stub end-to-end tests.
//!
//! These tests spin up a real boundary stub, splice the per-language
//! probe shim (which now carries the cap-specific
//! `__nyx_stub_*_record` helpers) ahead of a fixture's source, run the
//! resulting program with the stub's endpoint + recording-path env
//! vars set, then assert the stub captured the boundary event.
//!
//! Unlike `tests/stubs_per_cap.rs` (which synthesises harness
//! behaviour with host-side `SqlStub::record_query` calls), this suite
//! drives a real interpreter subprocess so the per-language shim
//! contract is exercised end-to-end. When the host is missing the
//! interpreter the test eprintln-skips, matching every other lang
//! fixture suite in-tree.
//!
//! Acceptance bullet from `.pitboss/play/deferred.md` Phase 10
//! follow-up: the Python+SQL pair is the cheapest first bite —
//! `sqlite3` is stdlib so no new toolchain dependency is required for
//! the dynamic CI matrix.
#![cfg(feature = "dynamic")]
use nyx_scanner::dynamic::lang::python::probe_shim as python_probe_shim;
use nyx_scanner::dynamic::stubs::{SqlStub, StubProvider};
use std::path::PathBuf;
use std::process::Command;
use tempfile::TempDir;
fn python3_available() -> bool {
Command::new("python3")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn fixture_path(rel: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("dynamic_fixtures")
.join("stubs_e2e")
.join(rel)
}
#[test]
fn python_sql_stub_captures_tautology_query_via_shim_recorder() {
if !python3_available() {
eprintln!("SKIP: python3 not available");
return;
}
let workdir = TempDir::new().expect("tempdir");
let stub = SqlStub::start(workdir.path()).expect("SqlStub::start");
// The verifier publishes the SQLite DB path on `NYX_SQL_ENDPOINT`
// (primary) and the queries-log path on `NYX_SQL_LOG` (companion).
let endpoint = stub.endpoint();
let recording = stub
.recording_endpoint()
.expect("SqlStub must publish a recording endpoint");
// Splice the probe shim ahead of the fixture source so the
// generated program carries the `__nyx_stub_sql_record` helper.
// Mirrors the production `PythonEmitter::emit` ordering.
let fixture =
std::fs::read_to_string(fixture_path("python/sql/vuln/main.py")).expect("read fixture");
let mut combined = String::with_capacity(python_probe_shim().len() + fixture.len() + 64);
combined.push_str(python_probe_shim());
combined.push_str("\n# ── fixture begins ─\n");
combined.push_str(&fixture);
let script_path = workdir.path().join("driver.py");
std::fs::write(&script_path, combined).expect("write driver");
let output = Command::new("python3")
.arg(&script_path)
.env("NYX_SQL_ENDPOINT", &endpoint)
.env(recording.0, &recording.1)
.output()
.expect("python3 driver");
assert!(
output.status.success(),
"driver must exit 0; stderr = {}",
String::from_utf8_lossy(&output.stderr)
);
let events = stub.drain_events();
assert!(
!events.is_empty(),
"SqlStub must capture at least one event after the shim recorder fires"
);
let tautology = events
.iter()
.find(|e| e.summary.contains("OR 1=1"))
.expect("recorded query must contain the tautology marker");
assert_eq!(
tautology.detail.get("driver").map(String::as_str),
Some("sqlite3"),
"kwargs passed to __nyx_stub_sql_record must surface as event detail entries"
);
}
#[test]
fn python_sql_shim_recorder_is_noop_without_log_env() {
if !python3_available() {
eprintln!("SKIP: python3 not available");
return;
}
let workdir = TempDir::new().expect("tempdir");
let stub = SqlStub::start(workdir.path()).expect("SqlStub::start");
// Drive the same fixture but withhold NYX_SQL_LOG. The shim
// helper must be a no-op so the same source still runs cleanly
// under harness modes that didn't spawn a stub.
let endpoint = stub.endpoint();
let fixture =
std::fs::read_to_string(fixture_path("python/sql/vuln/main.py")).expect("read fixture");
let mut combined = String::new();
combined.push_str(python_probe_shim());
combined.push('\n');
combined.push_str(&fixture);
let script_path = workdir.path().join("driver_no_log.py");
std::fs::write(&script_path, combined).expect("write driver");
let output = Command::new("python3")
.arg(&script_path)
.env("NYX_SQL_ENDPOINT", &endpoint)
.env_remove("NYX_SQL_LOG")
.output()
.expect("python3 driver");
assert!(
output.status.success(),
"driver must exit 0 even without NYX_SQL_LOG; stderr = {}",
String::from_utf8_lossy(&output.stderr)
);
let events = stub.drain_events();
assert!(
events.is_empty(),
"no events expected when the recording env var is unset, got {} entries",
events.len()
);
}