Skip to content

Commit 991c84a

Browse files
authored
Dynamic (#77)
1 parent 55247b7 commit 991c84a

1,464 files changed

Lines changed: 225448 additions & 1985 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.config/nextest.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# nextest configuration
2+
#
3+
# See https://nexte.st/docs/configuration/ for the full schema.
4+
5+
# ── Test groups ──────────────────────────────────────────────────────────────
6+
#
7+
# `hostile-input-timing` serialises the two timing-bounded
8+
# `hostile_input_tests` cases that pass under nextest in isolation but fail
9+
# under the full-suite parallel run on darwin (resource contention from the
10+
# other ~4000 tests pushes them past their internal budget). Pinning them to
11+
# a single thread within their own group keeps their wall-clock predictable
12+
# without slowing the rest of the suite.
13+
14+
[test-groups]
15+
hostile-input-timing = { max-threads = 1 }
16+
17+
[[profile.default.overrides]]
18+
filter = 'binary(hostile_input_tests) and (test(very_long_single_line_parses) or test(many_small_functions_do_not_explode))'
19+
test-group = 'hostile-input-timing'

.github/workflows/ci.yml

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88
branches: ["master"]
99
pull_request:
1010
branches: ["master"]
11+
workflow_dispatch:
1112

1213
concurrency:
1314
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -197,8 +198,8 @@ jobs:
197198
- name: Compile check at MSRV
198199
run: cargo check --all-features --tests
199200

200-
rust-stable-test:
201-
name: rust-stable-test
201+
rust-stable-test-linux-without-docker:
202+
name: rust-stable-test / linux-without-docker
202203
runs-on: ubuntu-latest
203204
steps:
204205
- uses: actions/checkout@v6
@@ -210,8 +211,59 @@ jobs:
210211

211212
- uses: taiki-e/install-action@nextest
212213

213-
- name: Rust tests (stable)
214-
run: cargo nextest run --all-features
214+
- name: Rust tests (stable, no docker)
215+
run: cargo nextest run --no-fail-fast --all-features
216+
217+
rust-stable-test-linux-with-docker:
218+
name: rust-stable-test / linux-with-docker
219+
runs-on: ubuntu-latest
220+
steps:
221+
- uses: actions/checkout@v6
222+
223+
- uses: actions-rust-lang/setup-rust-toolchain@v1
224+
with:
225+
toolchain: stable
226+
cache: true
227+
228+
- uses: taiki-e/install-action@nextest
229+
230+
- name: Pull language images for sandbox tests
231+
run: |
232+
docker pull python:3-slim
233+
docker pull node:20-slim
234+
docker pull eclipse-temurin:21-jre-jammy
235+
docker pull php:8-cli
236+
237+
- name: Smoke-test interpreter availability
238+
run: |
239+
docker run --rm python:3-slim python3 --version
240+
docker run --rm node:20-slim node --version
241+
docker run --rm eclipse-temurin:21-jre-jammy java -version
242+
docker run --rm php:8-cli php --version
243+
244+
- name: Rust tests with docker (sandbox escape gate)
245+
run: cargo nextest run --no-fail-fast --all-features --test dynamic_sandbox_escape --test dynamic_parity
246+
247+
escape-positive-control:
248+
name: escape-positive-control
249+
runs-on: ubuntu-latest
250+
steps:
251+
- uses: actions/checkout@v6
252+
253+
- uses: actions-rust-lang/setup-rust-toolchain@v1
254+
with:
255+
toolchain: stable
256+
cache: true
257+
258+
- uses: taiki-e/install-action@nextest
259+
260+
- name: Pull python image
261+
run: docker pull python:3-slim
262+
263+
- name: Escape positive control (gate wiring check)
264+
run: |
265+
cargo nextest run --no-fail-fast --all-features --test dynamic_sandbox_escape \
266+
-- --include-ignored positive_control_cap_sys_admin
215267
216268
cross-platform-smoke:
217269
name: cross-platform-smoke
@@ -234,7 +286,7 @@ jobs:
234286
run: cargo build --release --all-features
235287

236288
- name: Smoke tests
237-
run: cargo nextest run --all-features --test integration_tests --test pattern_tests --test cli_validation_tests
289+
run: cargo nextest run --no-fail-fast --all-features --test integration_tests --test pattern_tests --test cli_validation_tests
238290

239291
rust-beta-test:
240292
name: rust-beta-test
@@ -250,7 +302,7 @@ jobs:
250302
- uses: taiki-e/install-action@nextest
251303

252304
- name: Rust tests (beta)
253-
run: cargo nextest run --all-features
305+
run: cargo nextest run --no-fail-fast --all-features
254306

255307
cargo-package:
256308
name: cargo-package
@@ -299,16 +351,18 @@ jobs:
299351
cache: true
300352
cache-key: benchmark-gate-release
301353

354+
- uses: taiki-e/install-action@nextest
355+
302356
- name: Build benchmark + perf test binaries
303-
run: cargo test --release --all-features --test benchmark_test --test perf_tests --no-run
357+
run: cargo nextest run --release --all-features --test benchmark_test --test perf_tests --no-run
304358

305359
- name: Accuracy regression gate (P/R/F1)
306-
run: cargo test --release --all-features --test benchmark_test -- --ignored --nocapture benchmark_evaluation
360+
run: cargo nextest run --no-fail-fast --release --all-features --test benchmark_test --run-ignored only --no-capture benchmark_evaluation
307361

308362
- name: Performance regression gate
309363
env:
310364
NYX_CI_BENCH: "1"
311-
run: cargo test --release --all-features --test perf_tests -- --nocapture
365+
run: cargo nextest run --no-fail-fast --release --all-features --test perf_tests --no-capture
312366

313367
- name: Upload benchmark results
314368
if: always()
@@ -317,3 +371,34 @@ jobs:
317371
name: benchmark-results
318372
path: tests/benchmark/results/latest.json
319373
if-no-files-found: warn
374+
375+
corpus-marker-audit:
376+
name: corpus-marker-audit
377+
runs-on: ubuntu-latest
378+
steps:
379+
- uses: actions/checkout@v6
380+
381+
- uses: actions/setup-python@v5
382+
with:
383+
python-version: "3.12"
384+
385+
- name: Marker collision audit (§16.3)
386+
run: python3 scripts/corpus_dashboard.py
387+
# Exits non-zero if any oracle marker from one cap appears in another
388+
# cap's payload bytes. This catches cross-cap oracle collisions that
389+
# would cause false-positive confirmed verdicts.
390+
391+
- uses: actions-rust-lang/setup-rust-toolchain@v1
392+
with:
393+
toolchain: stable
394+
cache: true
395+
396+
- uses: taiki-e/install-action@nextest
397+
398+
- name: Corpus unit tests (no_marker_collisions, all_payloads_have_fixture_paths)
399+
run: cargo nextest run --no-fail-fast --lib -p nyx-scanner dynamic::corpus
400+
env:
401+
RUST_LOG: error
402+
403+
- name: Corpus dashboard sync check (Python/Rust payload table parity)
404+
run: python3 scripts/check_corpus_sync.py
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
name: Corpus Promote
2+
3+
# Weekly automated promotion-PR template.
4+
#
5+
# Scans fuzz-discovered/ for candidates not yet in src/dynamic/corpus.rs
6+
# and opens a PR proposing them for human review (§16.4 — no auto-merge).
7+
#
8+
# Also runs the marker-collision audit as a hard gate: if any collision is
9+
# found the workflow fails rather than proposing the promotion.
10+
11+
on:
12+
schedule:
13+
# Sundays at 09:00 UTC — offset from the fuzz run (06:00 UTC) so
14+
# discovered candidates are ready before the promotion job runs.
15+
- cron: "0 9 * * 0"
16+
workflow_dispatch:
17+
inputs:
18+
dry_run:
19+
description: "Dry run (print PR body but do not open)"
20+
required: false
21+
default: "false"
22+
23+
permissions:
24+
contents: write
25+
pull-requests: write
26+
27+
concurrency:
28+
group: corpus-promote
29+
cancel-in-progress: true
30+
31+
jobs:
32+
promote:
33+
name: Propose corpus promotions
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v6
37+
38+
- uses: actions-rust-lang/setup-rust-toolchain@v1
39+
with:
40+
toolchain: stable
41+
cache: true
42+
43+
- uses: actions/setup-node@v6
44+
with:
45+
node-version: 20
46+
cache: npm
47+
cache-dependency-path: frontend/package-lock.json
48+
49+
- name: Build frontend
50+
working-directory: frontend
51+
run: |
52+
npm ci
53+
npm run build
54+
55+
# ── Marker collision audit ──────────────────────────────────────────────
56+
- name: Marker collision audit
57+
run: |
58+
set -euo pipefail
59+
cargo build --features dynamic -p nyx-scanner 2>/dev/null || true
60+
cd fuzz/dynamic_corpus
61+
cargo run -- audit-markers
62+
env:
63+
RUST_LOG: error
64+
65+
# ── Discover candidates ─────────────────────────────────────────────────
66+
- name: Find promotion candidates
67+
id: candidates
68+
run: |
69+
set -euo pipefail
70+
count=0
71+
files=""
72+
if [ -d fuzz-discovered ]; then
73+
while IFS= read -r f; do
74+
# Skip .gitkeep, sidecar JSONs, and files already listed in corpus.rs.
75+
[[ "$f" == *".gitkeep" ]] && continue
76+
[[ "$f" == *".json" ]] && continue
77+
bytes=$(xxd -p "$f" | tr -d '\n')
78+
if ! grep -q "$bytes" src/dynamic/corpus.rs 2>/dev/null; then
79+
count=$((count + 1))
80+
files="$files $f"
81+
fi
82+
done < <(find fuzz-discovered -type f | sort)
83+
fi
84+
echo "count=$count" >> "$GITHUB_OUTPUT"
85+
echo "files=$files" >> "$GITHUB_OUTPUT"
86+
87+
- name: Skip if no new candidates
88+
if: steps.candidates.outputs.count == '0'
89+
run: |
90+
echo "No new candidates found in fuzz-discovered/. Nothing to promote."
91+
92+
# ── Open promotion PR ───────────────────────────────────────────────────
93+
- name: Open promotion PR
94+
if: >
95+
steps.candidates.outputs.count != '0' &&
96+
github.event.inputs.dry_run != 'true'
97+
env:
98+
GH_TOKEN: ${{ github.token }}
99+
CANDIDATE_COUNT: ${{ steps.candidates.outputs.count }}
100+
CANDIDATE_FILES: ${{ steps.candidates.outputs.files }}
101+
run: |
102+
set -euo pipefail
103+
branch="corpus-promote-$(date +%Y%m%d)"
104+
git checkout -b "$branch"
105+
106+
# Stage candidate files into fuzz-discovered (already there).
107+
# The PR body provides the reviewer with everything they need.
108+
109+
# Build PR body into a temp file to avoid shell re-interpolation of
110+
# sidecar JSON content (which may contain backticks or $(...) sequences).
111+
body_file=$(mktemp)
112+
113+
cat > "$body_file" <<'PREAMBLE'
114+
## Corpus Promotion Proposal
115+
116+
This PR was generated automatically by the weekly corpus-promote workflow.
117+
It does **not** auto-merge — a human reviewer must approve each candidate
118+
before it can land in `src/dynamic/corpus.rs` (§16.4).
119+
120+
### Candidates
121+
122+
The following payloads were discovered by the internal mutation fuzzer and
123+
confirmed via `sink_hit && oracle_fired` against instrumented fixtures:
124+
125+
PREAMBLE
126+
127+
for f in $CANDIDATE_FILES; do
128+
sidecar="${f}.json"
129+
printf -- '- `%s`\n' "$f" >> "$body_file"
130+
if [ -f "$sidecar" ]; then
131+
printf ' ```json\n' >> "$body_file"
132+
cat "$sidecar" >> "$body_file"
133+
printf '\n ```\n' >> "$body_file"
134+
fi
135+
done
136+
137+
cat >> "$body_file" <<'CHECKLIST'
138+
139+
### Review checklist
140+
141+
- [ ] Bytes are a genuine attack vector, not a fixture artifact
142+
- [ ] Oracle marker is unique (no collision with other caps)
143+
- [ ] `fixture_paths` updated in `src/dynamic/corpus.rs`
144+
- [ ] `since_corpus_version` set to next version
145+
- [ ] `CORPUS_VERSION` bumped and bump history updated
146+
147+
_Generated by corpus_promote.yml — do not auto-merge._
148+
CHECKLIST
149+
150+
git add fuzz-discovered/ || true
151+
git diff --cached --quiet || git commit -m "chore: add ${CANDIDATE_COUNT} fuzzer-discovered corpus candidates"
152+
153+
git push origin "$branch"
154+
155+
gh pr create \
156+
--title "chore(corpus): promote ${CANDIDATE_COUNT} fuzzer-discovered payload(s)" \
157+
--body "$(cat "$body_file")" \
158+
--base master \
159+
--label "corpus-promotion" || true
160+
161+
rm -f "$body_file"
162+
163+
- name: Dry run summary
164+
if: github.event.inputs.dry_run == 'true'
165+
run: |
166+
echo "Dry run: would promote ${{ steps.candidates.outputs.count }} candidate(s)."
167+
echo "Files: ${{ steps.candidates.outputs.files }}"

.github/workflows/docs.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ jobs:
2525
steps:
2626
- uses: actions/checkout@v6
2727

28+
- uses: actions-rust-lang/setup-rust-toolchain@v1
29+
with:
30+
toolchain: stable
31+
cache: true
32+
2833
- name: Cache mdbook
2934
id: cache-mdbook
3035
uses: actions/cache@v5

0 commit comments

Comments
 (0)