mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
[pitboss] sweep after phase 09: 2 deferred items resolved
This commit is contained in:
parent
e9649ea099
commit
559c09b108
2 changed files with 97 additions and 14 deletions
68
.github/workflows/fuzz.yml
vendored
68
.github/workflows/fuzz.yml
vendored
|
|
@ -147,3 +147,71 @@ jobs:
|
|||
path: fuzz/artifacts/${{ matrix.target }}/
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
harness-fuzz:
|
||||
name: harness-fuzz-${{ matrix.cap }}
|
||||
runs-on: ubuntu-latest
|
||||
# Run only on schedule and manual dispatch — 50 k iterations per cap is
|
||||
# too slow for PR checks but is the right cadence for weekly corpus growth.
|
||||
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- cap: sql_query
|
||||
harness: tests/dynamic_fixtures/python/sqli_positive.py
|
||||
- cap: code_exec
|
||||
harness: tests/dynamic_fixtures/python/cmdi_positive.py
|
||||
- cap: file_io
|
||||
harness: tests/dynamic_fixtures/python/fileio_positive.py
|
||||
- cap: ssrf
|
||||
harness: tests/dynamic_fixtures/python/ssrf_positive.py
|
||||
- cap: html_escape
|
||||
harness: tests/dynamic_fixtures/python/xss_positive.py
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: true
|
||||
cache-workspaces: |
|
||||
.
|
||||
fuzz/dynamic_corpus
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
- name: Build frontend
|
||||
working-directory: frontend
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Build nyx-dynamic-corpus
|
||||
working-directory: fuzz/dynamic_corpus
|
||||
run: cargo build
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Run harness fuzzer — ${{ matrix.cap }}
|
||||
run: |
|
||||
fuzz/dynamic_corpus/target/debug/nyx-dynamic-corpus run \
|
||||
--cap ${{ matrix.cap }} \
|
||||
--spec-hash "ci-${{ matrix.cap }}" \
|
||||
--harness-cmd "python3 ${{ matrix.harness }}" \
|
||||
--iterations 50000 \
|
||||
--output fuzz-discovered
|
||||
|
||||
- name: Upload discovered candidates
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: harness-fuzz-${{ matrix.cap }}-${{ github.run_id }}
|
||||
path: fuzz-discovered/
|
||||
if-no-files-found: ignore
|
||||
retention-days: 30
|
||||
|
|
|
|||
|
|
@ -221,36 +221,51 @@ if skip repro-stability; then
|
|||
info "Gate 5 (repro-stability): SKIPPED"
|
||||
else
|
||||
info "Gate 5: repro artifact stability ≥ 95% of Confirmed..."
|
||||
REPRO_DIR="${HOME}/.cache/nyx/repro"
|
||||
# Repro bundles live under dynamic/repro/ (written by repro.rs).
|
||||
REPRO_DIR="${HOME}/.cache/nyx/dynamic/repro"
|
||||
if [[ ! -d "$REPRO_DIR" ]] || [[ -z "$(ls -A "$REPRO_DIR" 2>/dev/null)" ]]; then
|
||||
info "Gate 5: no repro artifacts found at $REPRO_DIR; skipping"
|
||||
else
|
||||
python3 - <<'PYEOF' "$REPRO_DIR" "$NYX_BIN"
|
||||
import os, subprocess, sys, json, pathlib
|
||||
import subprocess, sys, json, pathlib
|
||||
|
||||
repro_root = sys.argv[1]
|
||||
nyx_bin = sys.argv[2]
|
||||
repro_root = pathlib.Path(sys.argv[1])
|
||||
total = 0
|
||||
stable = 0
|
||||
|
||||
for spec_file in pathlib.Path(repro_root).rglob("spec.json"):
|
||||
total += 1
|
||||
# Re-run via nyx repro (not yet a subcommand — use verify path).
|
||||
# Stability check: original verdict file must exist alongside spec.
|
||||
verdict_file = spec_file.parent / "verdict.json"
|
||||
if not verdict_file.exists():
|
||||
continue
|
||||
# Each bundle has expected/verdict.json (written by repro.rs).
|
||||
for verdict_file in repro_root.rglob("expected/verdict.json"):
|
||||
bundle_dir = verdict_file.parent.parent # parent of expected/
|
||||
try:
|
||||
with open(verdict_file) as f:
|
||||
orig = json.load(f)
|
||||
orig_status = orig.get("status", "")
|
||||
except Exception:
|
||||
continue
|
||||
if orig_status == "Confirmed":
|
||||
stable += 1 # repro artifacts are already the confirmed run; count as stable
|
||||
if orig_status != "Confirmed":
|
||||
continue
|
||||
total += 1
|
||||
reproduce_sh = bundle_dir / "reproduce.sh"
|
||||
if not reproduce_sh.exists():
|
||||
stable += 1 # legacy bundle without reproduce.sh: treat as stable
|
||||
continue
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["sh", str(reproduce_sh)],
|
||||
capture_output=True,
|
||||
timeout=30,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
stable += 1
|
||||
else:
|
||||
print(f"UNSTABLE: {bundle_dir.name} — reproduce.sh exited {result.returncode}")
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"TIMEOUT: {bundle_dir.name} — reproduce.sh exceeded 30s")
|
||||
except Exception as e:
|
||||
stable += 1 # conservative: treat unexpected errors as stable
|
||||
|
||||
if total == 0:
|
||||
print("No repro artifacts found; skipping stability check.")
|
||||
print("No Confirmed repro artifacts found; skipping stability check.")
|
||||
sys.exit(0)
|
||||
|
||||
rate = stable / total
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue