mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-21 20:18:06 +02:00
[pitboss] phase 11: Track J.9 + Track L.9 — CRYPTO, JSON_PARSE, UNAUTHORIZED_ID, DATA_EXFIL corpora
This commit is contained in:
parent
61a9e4e5df
commit
6784d73e25
85 changed files with 2508 additions and 30 deletions
44
src/dynamic/corpus/crypto/go.rs
Normal file
44
src/dynamic/corpus/crypto/go.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//! Go `Cap::CRYPTO` payloads — `math/rand.Intn` weak-key
|
||||
//! generation.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const WEAK_BITS: u32 = 16;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_WEAK",
|
||||
label: "crypto-go-weak-random",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/go/vuln.go"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "crypto-go-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_STRONG",
|
||||
label: "crypto-go-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/go/benign.go"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
55
src/dynamic/corpus/crypto/java.rs
Normal file
55
src/dynamic/corpus/crypto/java.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//! Java `Cap::CRYPTO` payloads — `java.util.Random.nextBytes`
|
||||
//! weak-key generation.
|
||||
//!
|
||||
//! Vuln payload: marker bytes that signal the harness to drive its
|
||||
//! `java.util.Random` key-generation path. The harness emits a key
|
||||
//! bounded inside a 16-bit search space and writes a
|
||||
//! [`crate::dynamic::probe::ProbeKind::WeakKey`] probe — the
|
||||
//! [`crate::dynamic::oracle::ProbePredicate::WeakKeyEntropy`]
|
||||
//! predicate fires for `key_int < 2^16`.
|
||||
//!
|
||||
//! Benign control: marker bytes that route the harness through
|
||||
//! `java.security.SecureRandom`, producing a 256-bit key whose
|
||||
//! integer view trivially exceeds the budget.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const WEAK_BITS: u32 = 16;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_WEAK",
|
||||
label: "crypto-java-weak-random",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/java/vuln.java"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "crypto-java-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_STRONG",
|
||||
label: "crypto-java-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/java/benign.java"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
26
src/dynamic/corpus/crypto/mod.rs
Normal file
26
src/dynamic/corpus/crypto/mod.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//! Weak-crypto (`Cap::CRYPTO`) per-language payload slices.
|
||||
//!
|
||||
//! Phase 11 (Track J.9) carves a weak-key entropy oracle across the
|
||||
//! five backend languages where homegrown key generation is common
|
||||
//! enough to matter: Java (`java.util.Random.nextBytes` → key bytes),
|
||||
//! Python (`random.randint(0, 0xFFFF)`), PHP (`mt_rand(0, 0xFFFF)`),
|
||||
//! Go (`math/rand.Intn(0x10000)`), Rust (`rand::thread_rng` truncated
|
||||
//! to 16 bits). Every vuln payload triggers the harness's
|
||||
//! instrumented key-generation path with a seed that produces an
|
||||
//! attacker-derivable key bounded inside the 16-bit search space.
|
||||
//! The harness shim writes a
|
||||
//! [`crate::dynamic::probe::ProbeKind::WeakKey { key_int }`] probe
|
||||
//! with the produced integer view of the key bytes; the
|
||||
//! [`crate::dynamic::oracle::ProbePredicate::WeakKeyEntropy`]
|
||||
//! predicate fires when `key_int < 2^max_bits` (`max_bits = 16` by
|
||||
//! default). The paired benign control routes the same harness
|
||||
//! through a CSPRNG (`SecureRandom`, `secrets.token_bytes`,
|
||||
//! `random_bytes(32)`, `crypto/rand.Read`, `rand::rngs::OsRng`) so
|
||||
//! the produced `key_int` trivially exceeds the budget and the
|
||||
//! predicate stays clear.
|
||||
|
||||
pub mod go;
|
||||
pub mod java;
|
||||
pub mod php;
|
||||
pub mod python;
|
||||
pub mod rust;
|
||||
43
src/dynamic/corpus/crypto/php.rs
Normal file
43
src/dynamic/corpus/crypto/php.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! PHP `Cap::CRYPTO` payloads — `mt_rand` weak-key generation.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const WEAK_BITS: u32 = 16;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_WEAK",
|
||||
label: "crypto-php-weak-random",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/php/vuln.php"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "crypto-php-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_STRONG",
|
||||
label: "crypto-php-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/php/benign.php"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
53
src/dynamic/corpus/crypto/python.rs
Normal file
53
src/dynamic/corpus/crypto/python.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//! Python `Cap::CRYPTO` payloads — `random.randint` weak-key
|
||||
//! generation.
|
||||
//!
|
||||
//! Vuln payload: marker bytes that route the harness through
|
||||
//! `random.randint(0, 0xFFFF)`; the harness emits a
|
||||
//! [`crate::dynamic::probe::ProbeKind::WeakKey`] probe and the
|
||||
//! [`crate::dynamic::oracle::ProbePredicate::WeakKeyEntropy`]
|
||||
//! predicate fires.
|
||||
//!
|
||||
//! Benign control: marker bytes that route the harness through
|
||||
//! `secrets.token_bytes(32)`.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const WEAK_BITS: u32 = 16;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_WEAK",
|
||||
label: "crypto-python-weak-random",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/python/vuln.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "crypto-python-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_STRONG",
|
||||
label: "crypto-python-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/python/benign.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
44
src/dynamic/corpus/crypto/rust.rs
Normal file
44
src/dynamic/corpus/crypto/rust.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//! Rust `Cap::CRYPTO` payloads — `rand::thread_rng` weak-key
|
||||
//! generation truncated to 16 bits.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const WEAK_BITS: u32 = 16;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_WEAK",
|
||||
label: "crypto-rust-weak-random",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/rust/vuln.rs"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "crypto-rust-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"NYX_CRYPTO_STRONG",
|
||||
label: "crypto-rust-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/crypto/rust/benign.rs"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
43
src/dynamic/corpus/data_exfil/go.rs
Normal file
43
src/dynamic/corpus/data_exfil/go.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! go `Cap::DATA_EXFIL` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const ALLOWLIST: &[&str] = &["127.0.0.1", "localhost"];
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"attacker.test",
|
||||
label: "data-exfil-go-external",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/go/vuln.go"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "data-exfil-go-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"127.0.0.1",
|
||||
label: "data-exfil-go-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/go/benign.go"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
43
src/dynamic/corpus/data_exfil/java.rs
Normal file
43
src/dynamic/corpus/data_exfil/java.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! java `Cap::DATA_EXFIL` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const ALLOWLIST: &[&str] = &["127.0.0.1", "localhost"];
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"attacker.test",
|
||||
label: "data-exfil-java-external",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/java/vuln.java"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "data-exfil-java-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"127.0.0.1",
|
||||
label: "data-exfil-java-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/java/benign.java"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
43
src/dynamic/corpus/data_exfil/js.rs
Normal file
43
src/dynamic/corpus/data_exfil/js.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! js `Cap::DATA_EXFIL` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const ALLOWLIST: &[&str] = &["127.0.0.1", "localhost"];
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"attacker.test",
|
||||
label: "data-exfil-js-external",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/js/vuln.js"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "data-exfil-js-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"127.0.0.1",
|
||||
label: "data-exfil-js-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/js/benign.js"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
22
src/dynamic/corpus/data_exfil/mod.rs
Normal file
22
src/dynamic/corpus/data_exfil/mod.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//! Data-exfiltration (`Cap::DATA_EXFIL`) per-language payload
|
||||
//! slices.
|
||||
//!
|
||||
//! Phase 11 (Track J.9) carves an outbound-network oracle across
|
||||
//! all seven backend-capable languages. Each harness stands up a
|
||||
//! mock HTTP client that records the destination host of every
|
||||
//! outbound request via a
|
||||
//! [`crate::dynamic::probe::ProbeKind::OutboundNetwork { host }`]
|
||||
//! probe. The
|
||||
//! [`crate::dynamic::oracle::ProbePredicate::OutboundHostNotIn`]
|
||||
//! predicate fires when the captured `host` falls outside the
|
||||
//! configured loopback allowlist (`&["127.0.0.1", "localhost"]`).
|
||||
//! The vuln payload supplies `attacker.test`; the paired benign
|
||||
//! control supplies `127.0.0.1` so the predicate stays clear.
|
||||
|
||||
pub mod go;
|
||||
pub mod java;
|
||||
pub mod js;
|
||||
pub mod php;
|
||||
pub mod python;
|
||||
pub mod ruby;
|
||||
pub mod rust;
|
||||
43
src/dynamic/corpus/data_exfil/php.rs
Normal file
43
src/dynamic/corpus/data_exfil/php.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! php `Cap::DATA_EXFIL` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const ALLOWLIST: &[&str] = &["127.0.0.1", "localhost"];
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"attacker.test",
|
||||
label: "data-exfil-php-external",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/php/vuln.php"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "data-exfil-php-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"127.0.0.1",
|
||||
label: "data-exfil-php-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/php/benign.php"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
43
src/dynamic/corpus/data_exfil/python.rs
Normal file
43
src/dynamic/corpus/data_exfil/python.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! python `Cap::DATA_EXFIL` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const ALLOWLIST: &[&str] = &["127.0.0.1", "localhost"];
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"attacker.test",
|
||||
label: "data-exfil-python-external",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/python/vuln.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "data-exfil-python-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"127.0.0.1",
|
||||
label: "data-exfil-python-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/python/benign.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
43
src/dynamic/corpus/data_exfil/ruby.rs
Normal file
43
src/dynamic/corpus/data_exfil/ruby.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! ruby `Cap::DATA_EXFIL` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const ALLOWLIST: &[&str] = &["127.0.0.1", "localhost"];
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"attacker.test",
|
||||
label: "data-exfil-ruby-external",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/ruby/vuln.rb"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "data-exfil-ruby-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"127.0.0.1",
|
||||
label: "data-exfil-ruby-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/ruby/benign.rb"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
43
src/dynamic/corpus/data_exfil/rust.rs
Normal file
43
src/dynamic/corpus/data_exfil/rust.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! rust `Cap::DATA_EXFIL` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const ALLOWLIST: &[&str] = &["127.0.0.1", "localhost"];
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"attacker.test",
|
||||
label: "data-exfil-rust-external",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/rust/vuln.rs"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "data-exfil-rust-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"127.0.0.1",
|
||||
label: "data-exfil-rust-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/data_exfil/rust/benign.rs"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
51
src/dynamic/corpus/json_parse/javascript.rs
Normal file
51
src/dynamic/corpus/json_parse/javascript.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//! JavaScript `Cap::JSON_PARSE` payloads — `JSON.parse` then deep
|
||||
//! assign / `Object.assign` chain.
|
||||
//!
|
||||
//! Same canary oracle as the Phase 10 PROTOTYPE_POLLUTION corpus
|
||||
//! ([`crate::dynamic::oracle::ProbePredicate::PrototypeCanaryTouched`]).
|
||||
//! The harness routes both payloads through `JSON.parse` first to
|
||||
//! exercise the parse-then-assign flow specifically (whereas the
|
||||
//! Phase 10 corpus passes the JSON literal directly to the deep-merge
|
||||
//! sink without an intervening parse).
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const CANARY: &str = "__nyx_canary";
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: br#"{"__proto__":{"__nyx_canary":"pwned"}}"#,
|
||||
label: "json-parse-js-proto-key",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/json_parse/javascript/vuln.js"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "json-parse-js-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: br#"{"data":{"__nyx_canary":"pwned"}}"#,
|
||||
label: "json-parse-js-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/json_parse/javascript/benign.js"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
21
src/dynamic/corpus/json_parse/mod.rs
Normal file
21
src/dynamic/corpus/json_parse/mod.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//! JSON-parse pollution (`Cap::JSON_PARSE`) per-language payload
|
||||
//! slices.
|
||||
//!
|
||||
//! Phase 11 (Track J.9) reuses the prototype-canary oracle from
|
||||
//! Phase 10 across the three languages whose JSON parsers have a
|
||||
//! published pollution surface: JavaScript (`JSON.parse` then deep
|
||||
//! assign), Python (`json.loads` then `dict.update` /
|
||||
//! `setattr`-driven attribute pollution), Ruby (`JSON.parse` then
|
||||
//! recursive merge). Every vuln payload binds a JSON literal whose
|
||||
//! top-level key is `__proto__`; the per-language harness's
|
||||
//! instrumented canary trap (`Object.prototype.__nyx_canary` in JS,
|
||||
//! a `dict`/class-scoped sentinel in Python, an `Object.prepend`
|
||||
//! flag in Ruby) records a
|
||||
//! [`crate::dynamic::probe::ProbeKind::PrototypePollution`] probe
|
||||
//! once the malicious key reaches the shared chain. The paired
|
||||
//! benign control sends a JSON literal whose top-level key is the
|
||||
//! regular property `data`, leaving the chain untouched.
|
||||
|
||||
pub mod javascript;
|
||||
pub mod python;
|
||||
pub mod ruby;
|
||||
45
src/dynamic/corpus/json_parse/python.rs
Normal file
45
src/dynamic/corpus/json_parse/python.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//! Python `Cap::JSON_PARSE` payloads — `json.loads` then
|
||||
//! attribute-pollution via `setattr` / `dict.update` on a shared
|
||||
//! sentinel object.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const CANARY: &str = "__nyx_canary";
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: br#"{"__proto__":{"__nyx_canary":"pwned"}}"#,
|
||||
label: "json-parse-python-proto-key",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/json_parse/python/vuln.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "json-parse-python-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: br#"{"data":{"__nyx_canary":"pwned"}}"#,
|
||||
label: "json-parse-python-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/json_parse/python/benign.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
44
src/dynamic/corpus/json_parse/ruby.rs
Normal file
44
src/dynamic/corpus/json_parse/ruby.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//! Ruby `Cap::JSON_PARSE` payloads — `JSON.parse` then recursive
|
||||
//! `Hash#deep_merge!` on a shared sentinel object.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
const CANARY: &str = "__nyx_canary";
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: br#"{"__proto__":{"__nyx_canary":"pwned"}}"#,
|
||||
label: "json-parse-ruby-proto-key",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/json_parse/ruby/vuln.rb"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "json-parse-ruby-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: br#"{"data":{"__nyx_canary":"pwned"}}"#,
|
||||
label: "json-parse-ruby-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::PrototypeCanaryTouched { canary: CANARY }],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/json_parse/ruby/benign.rb"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
|
|
@ -24,8 +24,9 @@ use std::collections::HashMap;
|
|||
use std::sync::OnceLock;
|
||||
|
||||
use super::{
|
||||
cmdi, deserialize, fmt_string, header_injection, ldap, open_redirect, path_trav,
|
||||
prototype_pollution, sqli, ssrf, ssti, xpath, xss, xxe,
|
||||
cmdi, crypto, data_exfil, deserialize, fmt_string, header_injection, json_parse, ldap,
|
||||
open_redirect, path_trav, prototype_pollution, sqli, ssrf, ssti, unauthorized_id, xpath, xss,
|
||||
xxe,
|
||||
};
|
||||
use super::{CapCorpus, CuratedPayload, Oracle};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
|
@ -36,13 +37,42 @@ use crate::symbol::Lang;
|
|||
/// and sinks we cannot yet model with a reliable oracle. The
|
||||
/// [`super::audit`] module asserts that the union of caps covered by
|
||||
/// [`CORPUS::entries`] and this constant equals [`Cap::all`].
|
||||
pub const CORPUS_UNSUPPORTED_LANG_NEUTRAL: u32 = Cap::ENV_VAR.bits()
|
||||
| Cap::SHELL_ESCAPE.bits()
|
||||
| Cap::URL_ENCODE.bits()
|
||||
| Cap::JSON_PARSE.bits()
|
||||
| Cap::CRYPTO.bits()
|
||||
| Cap::UNAUTHORIZED_ID.bits()
|
||||
| Cap::DATA_EXFIL.bits();
|
||||
///
|
||||
/// Phase 11 (Track J.9) carved `CRYPTO`, `JSON_PARSE`,
|
||||
/// `UNAUTHORIZED_ID`, and `DATA_EXFIL` corpora; the remaining caps
|
||||
/// here (`ENV_VAR`, `SHELL_ESCAPE`, `URL_ENCODE`) are pure
|
||||
/// sources / sanitizers with no sink behaviour and route through
|
||||
/// [`crate::evidence::UnsupportedReason::SoundOracleUnavailable`]
|
||||
/// at run time.
|
||||
pub const CORPUS_UNSUPPORTED_LANG_NEUTRAL: u32 =
|
||||
Cap::ENV_VAR.bits() | Cap::SHELL_ESCAPE.bits() | Cap::URL_ENCODE.bits();
|
||||
|
||||
/// Caps for which no sound oracle exists — emitted as
|
||||
/// [`crate::evidence::UnsupportedReason::SoundOracleUnavailable`]
|
||||
/// instead of [`crate::evidence::UnsupportedReason::NoPayloadsForCap`]
|
||||
/// so the unsupported budget accounting reflects the structural
|
||||
/// impossibility rather than a missing-payload gap. Currently the
|
||||
/// same set as [`CORPUS_UNSUPPORTED_LANG_NEUTRAL`]; kept as a
|
||||
/// distinct constant so future caps that legitimately cannot be
|
||||
/// oracled (e.g. side-channel timing) can land here without
|
||||
/// expanding the lang-neutral unsupported set.
|
||||
pub const CORPUS_SOUND_ORACLE_UNAVAILABLE: u32 =
|
||||
Cap::ENV_VAR.bits() | Cap::SHELL_ESCAPE.bits() | Cap::URL_ENCODE.bits();
|
||||
|
||||
/// Human-actionable hint for [`CORPUS_SOUND_ORACLE_UNAVAILABLE`]
|
||||
/// caps, surfaced via
|
||||
/// [`crate::evidence::UnsupportedReason::SoundOracleUnavailable::hint`].
|
||||
pub fn sound_oracle_unavailable_hint(cap: Cap) -> &'static str {
|
||||
if cap == Cap::ENV_VAR {
|
||||
"ENV_VAR is a source cap with no externally-observable sink behaviour"
|
||||
} else if cap == Cap::SHELL_ESCAPE {
|
||||
"SHELL_ESCAPE is a sanitizer cap whose effect is observed at the wrapping sink"
|
||||
} else if cap == Cap::URL_ENCODE {
|
||||
"URL_ENCODE is a sanitizer cap whose effect is observed at the wrapping sink"
|
||||
} else {
|
||||
"no sound oracle is currently available for this cap"
|
||||
}
|
||||
}
|
||||
|
||||
/// Flat `(Cap, Lang, slice)` table. A single cap can carry per-language
|
||||
/// variants — that's the whole reason this layer exists.
|
||||
|
|
@ -98,6 +128,28 @@ const ENTRIES: &[(Cap, Lang, &[CuratedPayload])] = &[
|
|||
Lang::TypeScript,
|
||||
prototype_pollution::typescript::PAYLOADS,
|
||||
),
|
||||
(Cap::CRYPTO, Lang::Java, crypto::java::PAYLOADS),
|
||||
(Cap::CRYPTO, Lang::Python, crypto::python::PAYLOADS),
|
||||
(Cap::CRYPTO, Lang::Php, crypto::php::PAYLOADS),
|
||||
(Cap::CRYPTO, Lang::Go, crypto::go::PAYLOADS),
|
||||
(Cap::CRYPTO, Lang::Rust, crypto::rust::PAYLOADS),
|
||||
(Cap::JSON_PARSE, Lang::JavaScript, json_parse::javascript::PAYLOADS),
|
||||
(Cap::JSON_PARSE, Lang::Python, json_parse::python::PAYLOADS),
|
||||
(Cap::JSON_PARSE, Lang::Ruby, json_parse::ruby::PAYLOADS),
|
||||
(Cap::UNAUTHORIZED_ID, Lang::Python, unauthorized_id::python::PAYLOADS),
|
||||
(Cap::UNAUTHORIZED_ID, Lang::Ruby, unauthorized_id::ruby::PAYLOADS),
|
||||
(Cap::UNAUTHORIZED_ID, Lang::Java, unauthorized_id::java::PAYLOADS),
|
||||
(Cap::UNAUTHORIZED_ID, Lang::Php, unauthorized_id::php::PAYLOADS),
|
||||
(Cap::UNAUTHORIZED_ID, Lang::JavaScript, unauthorized_id::js::PAYLOADS),
|
||||
(Cap::UNAUTHORIZED_ID, Lang::Go, unauthorized_id::go::PAYLOADS),
|
||||
(Cap::UNAUTHORIZED_ID, Lang::Rust, unauthorized_id::rust::PAYLOADS),
|
||||
(Cap::DATA_EXFIL, Lang::Python, data_exfil::python::PAYLOADS),
|
||||
(Cap::DATA_EXFIL, Lang::Ruby, data_exfil::ruby::PAYLOADS),
|
||||
(Cap::DATA_EXFIL, Lang::Java, data_exfil::java::PAYLOADS),
|
||||
(Cap::DATA_EXFIL, Lang::Php, data_exfil::php::PAYLOADS),
|
||||
(Cap::DATA_EXFIL, Lang::JavaScript, data_exfil::js::PAYLOADS),
|
||||
(Cap::DATA_EXFIL, Lang::Go, data_exfil::go::PAYLOADS),
|
||||
(Cap::DATA_EXFIL, Lang::Rust, data_exfil::rust::PAYLOADS),
|
||||
];
|
||||
|
||||
/// Reserved for per-cap oracle defaults. Empty in Phase 02; populated by
|
||||
|
|
@ -312,19 +364,18 @@ mod tests {
|
|||
assert!(!payloads_for(Cap::HEADER_INJECTION).is_empty());
|
||||
assert!(!payloads_for(Cap::OPEN_REDIRECT).is_empty());
|
||||
assert!(!payloads_for(Cap::PROTOTYPE_POLLUTION).is_empty());
|
||||
assert!(!payloads_for(Cap::CRYPTO).is_empty());
|
||||
assert!(!payloads_for(Cap::JSON_PARSE).is_empty());
|
||||
assert!(!payloads_for(Cap::UNAUTHORIZED_ID).is_empty());
|
||||
assert!(!payloads_for(Cap::DATA_EXFIL).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_caps_return_empty() {
|
||||
let unsupported = [
|
||||
Cap::ENV_VAR,
|
||||
Cap::SHELL_ESCAPE,
|
||||
Cap::URL_ENCODE,
|
||||
Cap::JSON_PARSE,
|
||||
Cap::CRYPTO,
|
||||
Cap::UNAUTHORIZED_ID,
|
||||
Cap::DATA_EXFIL,
|
||||
];
|
||||
// Phase 11 (Track J.9): only pure-source / pure-sanitizer
|
||||
// caps remain unsupported. CRYPTO / JSON_PARSE /
|
||||
// UNAUTHORIZED_ID / DATA_EXFIL now carry payloads.
|
||||
let unsupported = [Cap::ENV_VAR, Cap::SHELL_ESCAPE, Cap::URL_ENCODE];
|
||||
for cap in unsupported {
|
||||
assert!(
|
||||
payloads_for(cap).is_empty(),
|
||||
|
|
@ -333,6 +384,62 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn phase_11_caps_have_payloads() {
|
||||
assert!(!payloads_for(Cap::CRYPTO).is_empty());
|
||||
assert!(!payloads_for(Cap::JSON_PARSE).is_empty());
|
||||
assert!(!payloads_for(Cap::UNAUTHORIZED_ID).is_empty());
|
||||
assert!(!payloads_for(Cap::DATA_EXFIL).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn phase_11_caps_pair_benign_controls_per_lang() {
|
||||
let cases: &[(Cap, &[Lang])] = &[
|
||||
(Cap::CRYPTO, &[Lang::Java, Lang::Python, Lang::Php, Lang::Go, Lang::Rust]),
|
||||
(Cap::JSON_PARSE, &[Lang::JavaScript, Lang::Python, Lang::Ruby]),
|
||||
(
|
||||
Cap::UNAUTHORIZED_ID,
|
||||
&[
|
||||
Lang::Python,
|
||||
Lang::Ruby,
|
||||
Lang::Java,
|
||||
Lang::Php,
|
||||
Lang::JavaScript,
|
||||
Lang::Go,
|
||||
Lang::Rust,
|
||||
],
|
||||
),
|
||||
(
|
||||
Cap::DATA_EXFIL,
|
||||
&[
|
||||
Lang::Python,
|
||||
Lang::Ruby,
|
||||
Lang::Java,
|
||||
Lang::Php,
|
||||
Lang::JavaScript,
|
||||
Lang::Go,
|
||||
Lang::Rust,
|
||||
],
|
||||
),
|
||||
];
|
||||
for (cap, langs) in cases {
|
||||
for lang in *langs {
|
||||
let slice = payloads_for_lang(*cap, *lang);
|
||||
assert!(
|
||||
!slice.is_empty(),
|
||||
"({cap:?}, {lang:?}) must have payloads",
|
||||
);
|
||||
let vuln = slice
|
||||
.iter()
|
||||
.find(|p| !p.is_benign)
|
||||
.unwrap_or_else(|| panic!("missing vuln for ({cap:?}, {lang:?})"));
|
||||
let resolved = resolve_benign_control_lang(vuln, *cap, *lang)
|
||||
.unwrap_or_else(|| panic!("missing benign for ({cap:?}, {lang:?})"));
|
||||
assert!(resolved.is_benign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fileio_has_benign_payload() {
|
||||
assert!(benign_payload_for(Cap::FILE_IO).is_some());
|
||||
|
|
@ -359,6 +466,10 @@ mod tests {
|
|||
Cap::HEADER_INJECTION,
|
||||
Cap::OPEN_REDIRECT,
|
||||
Cap::PROTOTYPE_POLLUTION,
|
||||
Cap::CRYPTO,
|
||||
Cap::JSON_PARSE,
|
||||
Cap::UNAUTHORIZED_ID,
|
||||
Cap::DATA_EXFIL,
|
||||
] {
|
||||
let has_vuln = payloads_for(cap).iter().any(|p| !p.is_benign);
|
||||
assert!(has_vuln, "{cap:?} must have at least one vuln payload");
|
||||
|
|
@ -413,6 +524,10 @@ mod tests {
|
|||
Cap::HEADER_INJECTION,
|
||||
Cap::OPEN_REDIRECT,
|
||||
Cap::PROTOTYPE_POLLUTION,
|
||||
Cap::CRYPTO,
|
||||
Cap::JSON_PARSE,
|
||||
Cap::UNAUTHORIZED_ID,
|
||||
Cap::DATA_EXFIL,
|
||||
];
|
||||
for cap in caps {
|
||||
for p in payloads_for(cap) {
|
||||
|
|
@ -442,6 +557,10 @@ mod tests {
|
|||
Cap::HEADER_INJECTION,
|
||||
Cap::OPEN_REDIRECT,
|
||||
Cap::PROTOTYPE_POLLUTION,
|
||||
Cap::CRYPTO,
|
||||
Cap::JSON_PARSE,
|
||||
Cap::UNAUTHORIZED_ID,
|
||||
Cap::DATA_EXFIL,
|
||||
];
|
||||
for cap in caps {
|
||||
for p in payloads_for(cap) {
|
||||
|
|
@ -558,6 +677,10 @@ mod tests {
|
|||
Cap::HEADER_INJECTION,
|
||||
Cap::OPEN_REDIRECT,
|
||||
Cap::PROTOTYPE_POLLUTION,
|
||||
Cap::CRYPTO,
|
||||
Cap::JSON_PARSE,
|
||||
Cap::UNAUTHORIZED_ID,
|
||||
Cap::DATA_EXFIL,
|
||||
];
|
||||
for cap in caps {
|
||||
for p in payloads_for(cap).iter().filter(|p| p.is_benign) {
|
||||
|
|
|
|||
41
src/dynamic/corpus/unauthorized_id/go.rs
Normal file
41
src/dynamic/corpus/unauthorized_id/go.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! go `Cap::UNAUTHORIZED_ID` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"bob",
|
||||
label: "idor-go-cross-tenant",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/go/vuln.go"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "idor-go-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"alice",
|
||||
label: "idor-go-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/go/benign.go"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
41
src/dynamic/corpus/unauthorized_id/java.rs
Normal file
41
src/dynamic/corpus/unauthorized_id/java.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! java `Cap::UNAUTHORIZED_ID` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"bob",
|
||||
label: "idor-java-cross-tenant",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/java/vuln.java"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "idor-java-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"alice",
|
||||
label: "idor-java-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/java/benign.java"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
41
src/dynamic/corpus/unauthorized_id/js.rs
Normal file
41
src/dynamic/corpus/unauthorized_id/js.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! js `Cap::UNAUTHORIZED_ID` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"bob",
|
||||
label: "idor-js-cross-tenant",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/js/vuln.js"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "idor-js-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"alice",
|
||||
label: "idor-js-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/js/benign.js"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
23
src/dynamic/corpus/unauthorized_id/mod.rs
Normal file
23
src/dynamic/corpus/unauthorized_id/mod.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//! IDOR / unauthorized-id-access (`Cap::UNAUTHORIZED_ID`)
|
||||
//! per-language payload slices.
|
||||
//!
|
||||
//! Phase 11 (Track J.9) carves an IDOR oracle across all seven
|
||||
//! backend-capable languages. Each harness stands up a mock data
|
||||
//! store keyed by `owner_id` and a hard-coded `caller_id`
|
||||
//! (`"alice"`). The vuln payload supplies an `owner_id` that
|
||||
//! belongs to another user (`"bob"`); the harness's instrumented
|
||||
//! lookup returns the record without an authorization check and
|
||||
//! writes a [`crate::dynamic::probe::ProbeKind::IdorAccess { caller_id,
|
||||
//! owner_id }`] probe. The
|
||||
//! [`crate::dynamic::oracle::ProbePredicate::IdorBoundaryCrossed`]
|
||||
//! predicate fires whenever `caller_id != owner_id`. The paired
|
||||
//! benign control asks for the caller's own record (`"alice"`), so
|
||||
//! the probe records matching ids and the predicate stays clear.
|
||||
|
||||
pub mod go;
|
||||
pub mod java;
|
||||
pub mod js;
|
||||
pub mod php;
|
||||
pub mod python;
|
||||
pub mod ruby;
|
||||
pub mod rust;
|
||||
41
src/dynamic/corpus/unauthorized_id/php.rs
Normal file
41
src/dynamic/corpus/unauthorized_id/php.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! php `Cap::UNAUTHORIZED_ID` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"bob",
|
||||
label: "idor-php-cross-tenant",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/php/vuln.php"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "idor-php-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"alice",
|
||||
label: "idor-php-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/php/benign.php"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
41
src/dynamic/corpus/unauthorized_id/python.rs
Normal file
41
src/dynamic/corpus/unauthorized_id/python.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! Python `Cap::UNAUTHORIZED_ID` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"bob",
|
||||
label: "idor-python-cross-tenant",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/python/vuln.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "idor-python-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"alice",
|
||||
label: "idor-python-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/python/benign.py"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
41
src/dynamic/corpus/unauthorized_id/ruby.rs
Normal file
41
src/dynamic/corpus/unauthorized_id/ruby.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! ruby `Cap::UNAUTHORIZED_ID` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"bob",
|
||||
label: "idor-ruby-cross-tenant",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/ruby/vuln.rb"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "idor-ruby-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"alice",
|
||||
label: "idor-ruby-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/ruby/benign.rb"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
41
src/dynamic/corpus/unauthorized_id/rust.rs
Normal file
41
src/dynamic/corpus/unauthorized_id/rust.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! rust `Cap::UNAUTHORIZED_ID` payloads.
|
||||
|
||||
use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
||||
use crate::dynamic::oracle::ProbePredicate;
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
CuratedPayload {
|
||||
bytes: b"bob",
|
||||
label: "idor-rust-cross-tenant",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: false,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/rust/vuln.rs"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
benign_control: Some(PayloadRef {
|
||||
label: "idor-rust-benign",
|
||||
}),
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
CuratedPayload {
|
||||
bytes: b"alice",
|
||||
label: "idor-rust-benign",
|
||||
oracle: Oracle::SinkProbe {
|
||||
predicates: &[ProbePredicate::IdorBoundaryCrossed],
|
||||
},
|
||||
is_benign: true,
|
||||
provenance: PayloadProvenance::Curated,
|
||||
since_corpus_version: 15,
|
||||
deprecated_at_corpus_version: None,
|
||||
fixture_paths: &["tests/dynamic_fixtures/unauthorized_id/rust/benign.rs"],
|
||||
oob_nonce_slot: false,
|
||||
probe_predicates: &[],
|
||||
benign_control: None,
|
||||
no_benign_control_rationale: None,
|
||||
},
|
||||
];
|
||||
Loading…
Add table
Add a link
Reference in a new issue