mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
style(all): reformat long lines across files for improved code readability and alignment of nested structures
This commit is contained in:
parent
e64fb25dae
commit
9914d26bdf
20 changed files with 229 additions and 182 deletions
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
|
|
@ -216,13 +216,6 @@ jobs:
|
|||
rust-stable-test-linux-with-docker:
|
||||
name: rust-stable-test / linux-with-docker
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
docker:
|
||||
image: docker:dind
|
||||
options: --privileged
|
||||
env:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
|
|
@ -253,13 +246,6 @@ jobs:
|
|||
escape-positive-control:
|
||||
name: escape-positive-control
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
docker:
|
||||
image: docker:dind
|
||||
options: --privileged
|
||||
env:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
|
|
@ -364,16 +350,18 @@ jobs:
|
|||
cache: true
|
||||
cache-key: benchmark-gate-release
|
||||
|
||||
- uses: taiki-e/install-action@nextest
|
||||
|
||||
- name: Build benchmark + perf test binaries
|
||||
run: cargo test --release --all-features --test benchmark_test --test perf_tests --no-run
|
||||
run: cargo nextest run --release --all-features --test benchmark_test --test perf_tests --no-run
|
||||
|
||||
- name: Accuracy regression gate (P/R/F1)
|
||||
run: cargo test --release --all-features --test benchmark_test -- --ignored --nocapture benchmark_evaluation
|
||||
run: cargo nextest run --release --all-features --test benchmark_test --run-ignored only --no-capture benchmark_evaluation
|
||||
|
||||
- name: Performance regression gate
|
||||
env:
|
||||
NYX_CI_BENCH: "1"
|
||||
run: cargo test --release --all-features --test perf_tests -- --nocapture
|
||||
run: cargo nextest run --release --all-features --test perf_tests --no-capture
|
||||
|
||||
- name: Upload benchmark results
|
||||
if: always()
|
||||
|
|
@ -404,6 +392,8 @@ jobs:
|
|||
toolchain: stable
|
||||
cache: true
|
||||
|
||||
- uses: taiki-e/install-action@nextest
|
||||
|
||||
- name: Corpus unit tests (no_marker_collisions, all_payloads_have_fixture_paths)
|
||||
run: cargo nextest run --lib -p nyx-scanner dynamic::corpus
|
||||
env:
|
||||
|
|
|
|||
32
.github/workflows/corpus_promote.yml
vendored
32
.github/workflows/corpus_promote.yml
vendored
|
|
@ -111,18 +111,18 @@ jobs:
|
|||
body_file=$(mktemp)
|
||||
|
||||
cat > "$body_file" <<'PREAMBLE'
|
||||
## Corpus Promotion Proposal
|
||||
## Corpus Promotion Proposal
|
||||
|
||||
This PR was generated automatically by the weekly corpus-promote workflow.
|
||||
It does **not** auto-merge — a human reviewer must approve each candidate
|
||||
before it can land in `src/dynamic/corpus.rs` (§16.4).
|
||||
This PR was generated automatically by the weekly corpus-promote workflow.
|
||||
It does **not** auto-merge — a human reviewer must approve each candidate
|
||||
before it can land in `src/dynamic/corpus.rs` (§16.4).
|
||||
|
||||
### Candidates
|
||||
### Candidates
|
||||
|
||||
The following payloads were discovered by the internal mutation fuzzer and
|
||||
confirmed via `sink_hit && oracle_fired` against instrumented fixtures:
|
||||
The following payloads were discovered by the internal mutation fuzzer and
|
||||
confirmed via `sink_hit && oracle_fired` against instrumented fixtures:
|
||||
|
||||
PREAMBLE
|
||||
PREAMBLE
|
||||
|
||||
for f in $CANDIDATE_FILES; do
|
||||
sidecar="${f}.json"
|
||||
|
|
@ -136,16 +136,16 @@ PREAMBLE
|
|||
|
||||
cat >> "$body_file" <<'CHECKLIST'
|
||||
|
||||
### Review checklist
|
||||
### Review checklist
|
||||
|
||||
- [ ] Bytes are a genuine attack vector, not a fixture artifact
|
||||
- [ ] Oracle marker is unique (no collision with other caps)
|
||||
- [ ] `fixture_paths` updated in `src/dynamic/corpus.rs`
|
||||
- [ ] `since_corpus_version` set to next version
|
||||
- [ ] `CORPUS_VERSION` bumped and bump history updated
|
||||
- [ ] Bytes are a genuine attack vector, not a fixture artifact
|
||||
- [ ] Oracle marker is unique (no collision with other caps)
|
||||
- [ ] `fixture_paths` updated in `src/dynamic/corpus.rs`
|
||||
- [ ] `since_corpus_version` set to next version
|
||||
- [ ] `CORPUS_VERSION` bumped and bump history updated
|
||||
|
||||
_Generated by corpus_promote.yml — do not auto-merge._
|
||||
CHECKLIST
|
||||
_Generated by corpus_promote.yml — do not auto-merge._
|
||||
CHECKLIST
|
||||
|
||||
git add fuzz-discovered/ || true
|
||||
git diff --cached --quiet || git commit -m "chore: add ${CANDIDATE_COUNT} fuzzer-discovered corpus candidates"
|
||||
|
|
|
|||
9
.github/workflows/dynamic.yml
vendored
9
.github/workflows/dynamic.yml
vendored
|
|
@ -13,7 +13,7 @@
|
|||
# chroot-leg of the escape suite skips silently
|
||||
# (Phase 20 follow-up #4 in deferred.md).
|
||||
#
|
||||
# linux-with-docker — Ubuntu host with docker-in-docker. Exercises
|
||||
# linux-with-docker — Ubuntu host with the runner Docker daemon. Exercises
|
||||
# the docker backend (Phase 19) and the
|
||||
# differential-confirmation parity tests.
|
||||
#
|
||||
|
|
@ -79,13 +79,6 @@ jobs:
|
|||
linux-with-docker:
|
||||
name: dynamic / linux-with-docker
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
docker:
|
||||
image: docker:dind
|
||||
options: --privileged
|
||||
env:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@
|
|||
|
||||
<h2>Overview of licenses:</h2>
|
||||
<ul class="licenses-overview">
|
||||
<li><a href="#Apache-2.0">Apache License 2.0</a> (156)</li>
|
||||
<li><a href="#MIT">MIT License</a> (70)</li>
|
||||
<li><a href="#Apache-2.0">Apache License 2.0</a> (160)</li>
|
||||
<li><a href="#MIT">MIT License</a> (71)</li>
|
||||
<li><a href="#Zlib">zlib License</a> (2)</li>
|
||||
<li><a href="#BSD-2-Clause">BSD 2-Clause "Simplified" License</a> (1)</li>
|
||||
<li><a href="#BSD-3-Clause">BSD 3-Clause "New" or "Revised" License</a> (1)</li>
|
||||
|
|
@ -2638,6 +2638,7 @@ limitations under the License.</pre>
|
|||
<li><a href=" https://github.com/smol-rs/fastrand ">fastrand 2.4.1</a></li>
|
||||
<li><a href=" https://github.com/rust-lang/cc-rs ">find-msvc-tools 0.1.9</a></li>
|
||||
<li><a href=" https://github.com/petgraph/fixedbitset ">fixedbitset 0.5.7</a></li>
|
||||
<li><a href=" https://github.com/servo/rust-fnv ">fnv 1.0.7</a></li>
|
||||
<li><a href=" https://github.com/servo/rust-url ">form_urlencoded 1.2.2</a></li>
|
||||
<li><a href=" https://github.com/rust-lang/glob ">glob 0.3.3</a></li>
|
||||
<li><a href=" https://github.com/rust-lang/hashbrown ">hashbrown 0.14.5</a></li>
|
||||
|
|
@ -2661,6 +2662,8 @@ limitations under the License.</pre>
|
|||
<li><a href=" https://github.com/servo/rust-url/ ">percent-encoding 2.3.2</a></li>
|
||||
<li><a href=" https://github.com/petgraph/petgraph ">petgraph 0.8.3</a></li>
|
||||
<li><a href=" https://github.com/rust-lang/pkg-config-rs ">pkg-config 0.3.33</a></li>
|
||||
<li><a href=" https://github.com/tokio-rs/prost ">prost-derive 0.14.3</a></li>
|
||||
<li><a href=" https://github.com/tokio-rs/prost ">prost 0.14.3</a></li>
|
||||
<li><a href=" https://github.com/rayon-rs/rayon ">rayon-core 1.13.0</a></li>
|
||||
<li><a href=" https://github.com/rayon-rs/rayon ">rayon 1.12.0</a></li>
|
||||
<li><a href=" https://github.com/rust-lang/regex ">regex-automata 0.4.14</a></li>
|
||||
|
|
@ -4127,6 +4130,7 @@ limitations under the License.
|
|||
<h4>Used by:</h4>
|
||||
<ul class="license-used-by">
|
||||
<li><a href=" https://github.com/zrzka/anes-rs ">anes 0.1.6</a></li>
|
||||
<li><a href=" https://github.com/dtolnay/anyhow ">anyhow 1.0.102</a></li>
|
||||
<li><a href=" https://github.com/BLAKE3-team/BLAKE3 ">blake3 1.8.5</a></li>
|
||||
<li><a href=" https://github.com/cesarb/constant_time_eq ">constant_time_eq 0.4.2</a></li>
|
||||
<li><a href=" https://github.com/soc/directories-rs ">directories 6.0.0</a></li>
|
||||
|
|
@ -4557,7 +4561,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
<h3 id="GPL-3.0">GNU General Public License v3.0 only</h3>
|
||||
<h4>Used by:</h4>
|
||||
<ul class="license-used-by">
|
||||
<li><a href=" https://github.com/elicpeter/nyx ">nyx-scanner 0.7.0</a></li>
|
||||
<li><a href=" https://github.com/elicpeter/nyx ">nyx-scanner 0.8.0</a></li>
|
||||
</ul>
|
||||
<pre class="license-text">
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
|
@ -4894,6 +4898,39 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
</pre>
|
||||
</li>
|
||||
<li class="license">
|
||||
<h3 id="MIT">MIT License</h3>
|
||||
<h4>Used by:</h4>
|
||||
<ul class="license-used-by">
|
||||
<li><a href=" https://github.com/hyperium/h2 ">h2 0.4.14</a></li>
|
||||
</ul>
|
||||
<pre class="license-text">Copyright (c) 2017 h2 authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
</pre>
|
||||
</li>
|
||||
<li class="license">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ export function useTargets() {
|
|||
export function useAddTarget() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: { path: string }) => apiPost<TargetView>('/targets', body),
|
||||
mutationFn: (body: { path: string }) =>
|
||||
apiPost<TargetView>('/targets', body),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['targets'] });
|
||||
},
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ function verdictTooltip(verdict: VerifyResult): string {
|
|||
? `Not confirmed after ${verdict.attempts?.length ?? 0} payload attempt(s)`
|
||||
: 'Not confirmed';
|
||||
case 'Unsupported':
|
||||
return reason ? `Unsupported: ${reason}` : 'Dynamic verification not supported';
|
||||
return reason
|
||||
? `Unsupported: ${reason}`
|
||||
: 'Dynamic verification not supported';
|
||||
case 'Inconclusive':
|
||||
return inconclusive_reason
|
||||
? `Inconclusive: ${inconclusive_reason}${detail ? `: ${detail}` : ''}`
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ export function adaptSurfaceMap(data: SurfaceMap): GraphModel {
|
|||
const detail = nodeDetail(node);
|
||||
const searchText = [title, detail, loc.file].join(' ').toLowerCase();
|
||||
const authBadge =
|
||||
node.node === 'entry_point' && node.auth_required ? ['auth'] : undefined;
|
||||
node.node === 'entry_point' && node.auth_required
|
||||
? ['auth']
|
||||
: undefined;
|
||||
return {
|
||||
key: String(index),
|
||||
rawId: index,
|
||||
|
|
|
|||
|
|
@ -195,10 +195,7 @@ function cfgNodeStyle(
|
|||
}
|
||||
}
|
||||
|
||||
function surfaceNodeStyle(
|
||||
type: string,
|
||||
palette: GraphThemePalette,
|
||||
): NodeStyle {
|
||||
function surfaceNodeStyle(type: string, palette: GraphThemePalette): NodeStyle {
|
||||
switch (type) {
|
||||
case 'EntryPoint':
|
||||
return {
|
||||
|
|
@ -261,7 +258,11 @@ function surfaceNodeStyle(
|
|||
function surfaceEdgeStyle(type: string, palette: GraphThemePalette): EdgeStyle {
|
||||
switch (type) {
|
||||
case 'calls':
|
||||
return { color: withAlpha(palette.textSecondary, 0.78), width: 1.4, dash: [] };
|
||||
return {
|
||||
color: withAlpha(palette.textSecondary, 0.78),
|
||||
width: 1.4,
|
||||
dash: [],
|
||||
};
|
||||
case 'reads_from':
|
||||
return { color: palette.success, width: 1.5, dash: [] };
|
||||
case 'writes_to':
|
||||
|
|
|
|||
|
|
@ -733,7 +733,9 @@ export function DynamicVerdictSection({ verdict }: { verdict: VerifyResult }) {
|
|||
className="dynamic-toolchain-match"
|
||||
title={`Toolchain match: ${verdict.toolchain_match}`}
|
||||
>
|
||||
{verdict.toolchain_match === 'exact' ? 'exact toolchain' : 'approximate toolchain'}
|
||||
{verdict.toolchain_match === 'exact'
|
||||
? 'exact toolchain'
|
||||
: 'approximate toolchain'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -763,7 +765,8 @@ export function DynamicVerdictSection({ verdict }: { verdict: VerifyResult }) {
|
|||
)}
|
||||
{verdict.inconclusive_reason && (
|
||||
<div>
|
||||
<strong>Inconclusive reason:</strong> {verdict.inconclusive_reason}
|
||||
<strong>Inconclusive reason:</strong>{' '}
|
||||
{verdict.inconclusive_reason}
|
||||
</div>
|
||||
)}
|
||||
{verdict.detail && (
|
||||
|
|
@ -777,7 +780,10 @@ export function DynamicVerdictSection({ verdict }: { verdict: VerifyResult }) {
|
|||
<strong>Payload attempts:</strong>
|
||||
<ul className="dynamic-attempt-list">
|
||||
{attempts.map((a, i) => (
|
||||
<li key={i} className={`attempt-row ${a.triggered ? 'triggered' : ''}`}>
|
||||
<li
|
||||
key={i}
|
||||
className={`attempt-row ${a.triggered ? 'triggered' : ''}`}
|
||||
>
|
||||
<code>{a.payload_label}</code>
|
||||
<span className="attempt-outcome">
|
||||
{a.triggered
|
||||
|
|
|
|||
|
|
@ -781,7 +781,9 @@ export function FindingsPage() {
|
|||
</td>
|
||||
<td>
|
||||
<VerdictBadge
|
||||
verdict={f.dynamic_verdict ?? f.evidence?.dynamic_verdict}
|
||||
verdict={
|
||||
f.dynamic_verdict ?? f.evidence?.dynamic_verdict
|
||||
}
|
||||
compact
|
||||
/>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -307,8 +307,11 @@ function VerdictDiffSection({ data }: { data: CompareResponse }) {
|
|||
const entries = data.verdict_diff;
|
||||
if (!entries || entries.length === 0) {
|
||||
return (
|
||||
<div style={{ color: 'var(--text-secondary)', padding: 'var(--space-4)' }}>
|
||||
No verdict-level transitions. Both scans share no findings with stable hashes.
|
||||
<div
|
||||
style={{ color: 'var(--text-secondary)', padding: 'var(--space-4)' }}
|
||||
>
|
||||
No verdict-level transitions. Both scans share no findings with stable
|
||||
hashes.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -333,11 +336,16 @@ function VerdictDiffSection({ data }: { data: CompareResponse }) {
|
|||
<>
|
||||
<span
|
||||
className={`compare-finding-row ${TRANSITION_ROW_CLS[t]}`}
|
||||
style={{ padding: '0 var(--space-2)', borderRadius: 'var(--radius-sm)' }}
|
||||
style={{
|
||||
padding: '0 var(--space-2)',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
}}
|
||||
>
|
||||
{TRANSITION_LABELS[t]}
|
||||
</span>
|
||||
<span style={{ marginLeft: 'var(--space-2)' }}>({items.length})</span>
|
||||
<span style={{ marginLeft: 'var(--space-2)' }}>
|
||||
({items.length})
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
|
@ -345,7 +353,10 @@ function VerdictDiffSection({ data }: { data: CompareResponse }) {
|
|||
<div
|
||||
key={i}
|
||||
className={`compare-finding-row ${TRANSITION_ROW_CLS[t]}`}
|
||||
style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--text-xs)' }}
|
||||
style={{
|
||||
fontFamily: 'var(--font-mono)',
|
||||
fontSize: 'var(--text-xs)',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--text-tertiary)' }}>
|
||||
{e.path}:{e.line}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ function nodeSubtitle(node: SurfaceNode): string {
|
|||
}
|
||||
|
||||
function nodeLocation(node: SurfaceNode): string {
|
||||
const loc = node.node === 'entry_point' ? node.handler_location : node.location;
|
||||
const loc =
|
||||
node.node === 'entry_point' ? node.handler_location : node.location;
|
||||
return `${loc.file}:${loc.line}`;
|
||||
}
|
||||
|
||||
|
|
@ -158,9 +159,12 @@ function NeighborList({
|
|||
{EDGE_KIND_LABELS[e.kind]}
|
||||
</span>
|
||||
<span>
|
||||
{direction === 'in' ? '←' : '→'} <strong>{nodeTitle(other)}</strong>
|
||||
{direction === 'in' ? '←' : '→'}{' '}
|
||||
<strong>{nodeTitle(other)}</strong>
|
||||
</span>
|
||||
<code className="surface-neighbor-edge-loc">{nodeLocation(other)}</code>
|
||||
<code className="surface-neighbor-edge-loc">
|
||||
{nodeLocation(other)}
|
||||
</code>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
|
@ -250,7 +254,11 @@ export function SurfacePage() {
|
|||
<option value="external_service">External services</option>
|
||||
<option value="dangerous_local">Dangerous locals</option>
|
||||
</select>
|
||||
<div className="surface-view-toggle" role="tablist" aria-label="Surface view">
|
||||
<div
|
||||
className="surface-view-toggle"
|
||||
role="tablist"
|
||||
aria-label="Surface view"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ describe('VerdictBadge', () => {
|
|||
render(
|
||||
<VerdictBadge
|
||||
verdict={makeVerdict('PartiallyConfirmed', {
|
||||
detail: 'sink-reachability probe fired but the oracle marker was not observed',
|
||||
detail:
|
||||
'sink-reachability probe fired but the oracle marker was not observed',
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -102,10 +102,10 @@ impl JavacPool {
|
|||
// If a prior call torched the worker, try one re-spawn here so
|
||||
// the caller doesn't see consecutive failures from a transient
|
||||
// JVM crash.
|
||||
if guard.is_none() {
|
||||
if let Ok(w) = spawn_worker(&self.bootstrap_dir) {
|
||||
*guard = Some(w);
|
||||
}
|
||||
if guard.is_none()
|
||||
&& let Ok(w) = spawn_worker(&self.bootstrap_dir)
|
||||
{
|
||||
*guard = Some(w);
|
||||
}
|
||||
let worker = match guard.as_mut() {
|
||||
Some(w) => w,
|
||||
|
|
@ -419,8 +419,7 @@ fn decode_b64(s: &str) -> Option<String> {
|
|||
}
|
||||
let bytes: Vec<u8> = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
|
||||
let mut out = Vec::with_capacity(bytes.len() / 4 * 3);
|
||||
let mut iter = bytes.chunks(4);
|
||||
while let Some(chunk) = iter.next() {
|
||||
for chunk in bytes.chunks(4) {
|
||||
if chunk.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,14 +53,14 @@ impl BuildPool for RubyPool {
|
|||
let start = Instant::now();
|
||||
|
||||
// `bundle check` short-circuits when the host already has every gem.
|
||||
if let Ok(o) = self.bundle(workdir).arg("check").output() {
|
||||
if o.status.success() {
|
||||
return PoolCompileResult {
|
||||
success: true,
|
||||
stderr: String::new(),
|
||||
duration: start.elapsed(),
|
||||
};
|
||||
}
|
||||
if let Ok(o) = self.bundle(workdir).arg("check").output()
|
||||
&& o.status.success()
|
||||
{
|
||||
return PoolCompileResult {
|
||||
success: true,
|
||||
stderr: String::new(),
|
||||
duration: start.elapsed(),
|
||||
};
|
||||
}
|
||||
|
||||
// The install target is pinned to a writable vendor dir via
|
||||
|
|
|
|||
|
|
@ -100,16 +100,16 @@ pub fn prepare_rust(spec: &HarnessSpec, workdir: &Path) -> Result<BuildResult, B
|
|||
/// healthy pool is surfaced verbatim (no legacy re-run — it would fail the
|
||||
/// same way).
|
||||
fn build_rust_binary(workdir: &Path, binary_dest: &Path) -> Result<(), String> {
|
||||
if is_pool_enabled("rust") {
|
||||
if let Ok(pool) = RustPool::try_new() {
|
||||
let pool_args = [binary_dest.to_string_lossy().into_owned()];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
if is_pool_enabled("rust")
|
||||
&& let Ok(pool) = RustPool::try_new()
|
||||
{
|
||||
let pool_args = [binary_dest.to_string_lossy().into_owned()];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
}
|
||||
try_build_rust_binary(workdir, binary_dest)
|
||||
|
|
@ -496,15 +496,15 @@ pub fn prepare_ruby(spec: &HarnessSpec, workdir: &Path) -> Result<BuildResult, B
|
|||
/// Route Bundler through [`RubyPool`] (shared Bootsnap cache) when enabled,
|
||||
/// else the legacy `bundle check`/`install` path.
|
||||
fn bundle_install(workdir: &Path) -> Result<(), String> {
|
||||
if is_pool_enabled("ruby") {
|
||||
if let Ok(pool) = RubyPool::try_new() {
|
||||
let res = pool.compile_batch(workdir, &[]);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
if is_pool_enabled("ruby")
|
||||
&& let Ok(pool) = RubyPool::try_new()
|
||||
{
|
||||
let res = pool.compile_batch(workdir, &[]);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
}
|
||||
try_bundle_install(workdir)
|
||||
|
|
@ -672,15 +672,15 @@ pub fn prepare_node(spec: &HarnessSpec, workdir: &Path) -> Result<BuildResult, B
|
|||
/// Route `npm install` through [`NodePool`] (shared npm download cache) when
|
||||
/// enabled, else the legacy direct-spawn path.
|
||||
fn npm_install(workdir: &Path) -> Result<(), String> {
|
||||
if is_pool_enabled("node") {
|
||||
if let Ok(pool) = NodePool::try_new() {
|
||||
let res = pool.compile_batch(workdir, &[]);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
if is_pool_enabled("node")
|
||||
&& let Ok(pool) = NodePool::try_new()
|
||||
{
|
||||
let res = pool.compile_batch(workdir, &[]);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
}
|
||||
try_npm_install(workdir)
|
||||
|
|
@ -804,16 +804,16 @@ pub fn prepare_go(spec: &HarnessSpec, workdir: &Path) -> Result<BuildResult, Bui
|
|||
/// `GOMODCACHE`, `-trimpath -buildvcs=false`) when enabled, else the legacy
|
||||
/// per-workdir-cache path.
|
||||
fn build_go_binary(workdir: &Path, binary_dest: &Path) -> Result<(), String> {
|
||||
if is_pool_enabled("go") {
|
||||
if let Ok(pool) = GoPool::try_new() {
|
||||
let pool_args = [binary_dest.to_string_lossy().into_owned()];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
if is_pool_enabled("go")
|
||||
&& let Ok(pool) = GoPool::try_new()
|
||||
{
|
||||
let pool_args = [binary_dest.to_string_lossy().into_owned()];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
}
|
||||
try_build_go_binary(workdir, binary_dest)
|
||||
|
|
@ -1115,22 +1115,22 @@ fn try_compile_java_with_toolchain(
|
|||
// the direct-spawn legacy path so an operator with a broken JDK
|
||||
// install still gets a deterministic build error from `javac`
|
||||
// itself rather than from the pool wrapper.
|
||||
if is_pool_enabled("java") {
|
||||
if let Some(pool) = javac_pool_for(toolchain_id) {
|
||||
let result = pool.compile_batch(workdir, &args);
|
||||
if result.success {
|
||||
return finalize_java_compile(workdir, cache_path, lib_on_cp);
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
// The compile itself failed (real source error) -- surface
|
||||
// the worker's stderr verbatim.
|
||||
return Err(result.stderr);
|
||||
}
|
||||
// Worker crashed: drop the cached pool so the next call
|
||||
// re-spawns it, then fall through to the legacy direct-spawn
|
||||
// path so this build still has a chance to succeed.
|
||||
drop_javac_pool(toolchain_id);
|
||||
if is_pool_enabled("java")
|
||||
&& let Some(pool) = javac_pool_for(toolchain_id)
|
||||
{
|
||||
let result = pool.compile_batch(workdir, &args);
|
||||
if result.success {
|
||||
return finalize_java_compile(workdir, cache_path, lib_on_cp);
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
// The compile itself failed (real source error) -- surface
|
||||
// the worker's stderr verbatim.
|
||||
return Err(result.stderr);
|
||||
}
|
||||
// Worker crashed: drop the cached pool so the next call
|
||||
// re-spawns it, then fall through to the legacy direct-spawn
|
||||
// path so this build still has a chance to succeed.
|
||||
drop_javac_pool(toolchain_id);
|
||||
}
|
||||
|
||||
let javac = std::env::var("NYX_JAVAC_BIN").unwrap_or_else(|_| "javac".to_owned());
|
||||
|
|
@ -1372,15 +1372,15 @@ pub fn prepare_php(spec: &HarnessSpec, workdir: &Path) -> Result<BuildResult, Bu
|
|||
/// Route Composer through [`PhpPool`] (shared download cache + opcache
|
||||
/// file-cache warm) when enabled, else the legacy direct-spawn path.
|
||||
fn composer_install(workdir: &Path) -> Result<(), String> {
|
||||
if is_pool_enabled("php") {
|
||||
if let Ok(pool) = PhpPool::try_new() {
|
||||
let res = pool.compile_batch(workdir, &[]);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
if is_pool_enabled("php")
|
||||
&& let Ok(pool) = PhpPool::try_new()
|
||||
{
|
||||
let res = pool.compile_batch(workdir, &[]);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
}
|
||||
try_composer_install(workdir)
|
||||
|
|
@ -1486,19 +1486,19 @@ pub fn prepare_c(
|
|||
/// static-link toggle is forwarded so the pool can reproduce the
|
||||
/// Strict-profile `-static` fallback.
|
||||
fn build_c_binary(workdir: &Path, binary_dest: &Path, static_link: bool) -> Result<(), String> {
|
||||
if is_pool_enabled("c") {
|
||||
if let Ok(pool) = CPool::try_new() {
|
||||
let pool_args = [
|
||||
binary_dest.to_string_lossy().into_owned(),
|
||||
if static_link { "static" } else { "dynamic" }.to_owned(),
|
||||
];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
if is_pool_enabled("c")
|
||||
&& let Ok(pool) = CPool::try_new()
|
||||
{
|
||||
let pool_args = [
|
||||
binary_dest.to_string_lossy().into_owned(),
|
||||
if static_link { "static" } else { "dynamic" }.to_owned(),
|
||||
];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
}
|
||||
try_build_c_binary(workdir, binary_dest, static_link)
|
||||
|
|
@ -1654,16 +1654,16 @@ pub fn prepare_cpp(spec: &HarnessSpec, workdir: &Path) -> Result<BuildResult, Bu
|
|||
/// Route the C++ harness build through [`CppPool`] (`ccache` + shared object
|
||||
/// cache) when enabled, else the legacy direct-spawn `c++` path.
|
||||
fn build_cpp_binary(workdir: &Path, binary_dest: &Path) -> Result<(), String> {
|
||||
if is_pool_enabled("cpp") {
|
||||
if let Ok(pool) = CppPool::try_new() {
|
||||
let pool_args = [binary_dest.to_string_lossy().into_owned()];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
if is_pool_enabled("cpp")
|
||||
&& let Ok(pool) = CppPool::try_new()
|
||||
{
|
||||
let pool_args = [binary_dest.to_string_lossy().into_owned()];
|
||||
let res = pool.compile_batch(workdir, &pool_args);
|
||||
if res.success {
|
||||
return Ok(());
|
||||
}
|
||||
if pool.is_healthy() {
|
||||
return Err(res.stderr);
|
||||
}
|
||||
}
|
||||
try_build_cpp_binary(workdir, binary_dest)
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ use super::super::{CuratedPayload, Oracle, PayloadProvenance, PayloadRef};
|
|||
/// `../nyx_pt_canary` traversal resolves.
|
||||
pub const CANARY_FILENAME: &str = "nyx_pt_canary";
|
||||
|
||||
/// Canary file CONTENT — the collision-resistant FILE_IO marker. Alphanumeric
|
||||
/// + underscore so a faithful HTML/URL escaper leaves it intact when the
|
||||
/// fixture writes the read bytes to the response. NOT a substring of any
|
||||
/// payload path.
|
||||
/// Canary file content for the collision-resistant FILE_IO marker. It uses
|
||||
/// alphanumeric characters plus underscore, so a faithful HTML/URL escaper
|
||||
/// leaves it intact when the fixture writes the read bytes to the response.
|
||||
/// NOT a substring of any payload path.
|
||||
pub const CANARY_MARKER: &str = "NYX_PATHTRAVERSAL_R34D_a7f3c1d8";
|
||||
|
||||
pub const PAYLOADS: &[CuratedPayload] = &[
|
||||
|
|
|
|||
|
|
@ -2021,7 +2021,7 @@ mod tests {
|
|||
.all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()),
|
||||
"render must be lowercase hex: {r}",
|
||||
);
|
||||
assert!(Canary::ENTROPY_BITS >= 128);
|
||||
const { assert!(Canary::ENTROPY_BITS >= 128) };
|
||||
assert!(
|
||||
r.len() * 4 >= 128,
|
||||
"rendered canary clears the 128-bit floor"
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ unsafe extern "C" {
|
|||
target: *const i8,
|
||||
fstype: *const i8,
|
||||
flags: u64,
|
||||
data: *const i8,
|
||||
data: *const core::ffi::c_void,
|
||||
) -> i32;
|
||||
fn write(fd: i32, buf: *const u8, count: usize) -> isize;
|
||||
fn __errno_location() -> *mut i32;
|
||||
|
|
@ -319,10 +319,6 @@ fn apply_no_new_privs() -> PrimitiveStatus {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_unshare() -> PrimitiveStatus {
|
||||
apply_unshare_with_flags(CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS)
|
||||
}
|
||||
|
||||
fn apply_unshare_with_flags(flags: i32) -> PrimitiveStatus {
|
||||
// CLONE_NEWUSER must come first on most modern kernels so the
|
||||
// unprivileged caller can map uid/gid; CLONE_NEWPID + CLONE_NEWNS
|
||||
|
|
@ -388,9 +384,10 @@ struct BindMount {
|
|||
/// the [`HardeningOutcome`] wire record, so callers that care about the
|
||||
/// bind-mount succeeding gate on whether the harness produced output.
|
||||
///
|
||||
/// Called in pre_exec between [`apply_unshare`] and [`apply_chroot`] so
|
||||
/// the new mount namespace is private to the child + grandchildren and
|
||||
/// the workdir is still reachable at its host-side absolute path.
|
||||
/// Called in pre_exec after [`apply_unshare_with_flags`] and before
|
||||
/// [`apply_chroot`] so the new mount namespace is private to the child +
|
||||
/// grandchildren and the workdir is still reachable at its host-side absolute
|
||||
/// path.
|
||||
fn apply_bind_mounts(mounts: &[BindMount]) {
|
||||
let none = b"none\0";
|
||||
for m in mounts {
|
||||
|
|
|
|||
|
|
@ -130,10 +130,7 @@ fn corpus_canaries_use_placeholder_and_are_substitutable() {
|
|||
/// once for the oracle — and the two must agree).
|
||||
#[test]
|
||||
fn canary_entropy_and_determinism() {
|
||||
assert!(
|
||||
Canary::ENTROPY_BITS >= 128,
|
||||
"Canary::ENTROPY_BITS must clear the 128-bit floor",
|
||||
);
|
||||
const { assert!(Canary::ENTROPY_BITS >= 128) };
|
||||
|
||||
let bytes = Canary::generate("spec-hash-under-audit");
|
||||
assert_eq!(bytes.len(), 32, "canary is 256 bits of BLAKE3 output");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue