diff --git a/.github/skills/scapy-security-audit/SKILL.md b/.github/skills/scapy-security-audit/SKILL.md new file mode 100644 index 00000000000..bca73a32a0c --- /dev/null +++ b/.github/skills/scapy-security-audit/SKILL.md @@ -0,0 +1,198 @@ +--- +name: Scapy security audit +description: Use this when conducting a long-running autonomous security audit of Scapy, validating a suspected vulnerability, or preparing a source-backed security report. +--- + +# Scapy security audit + +Use this skill to run an autonomous, evidence-driven search for severe, non-obvious, exploitable vulnerabilities in Scapy. Prioritize remotely reachable flaws, meaningful trust-boundary violations, and combinations of primitives that form high-impact exploit chains. Continue after the first confirmed result while time remains for a more severe finding, an independent vulnerability family, or a stronger validated chain. + +## 1) Prepare the campaign + +- Use the scope and reporting policy in [`SECURITY.md`](../../../SECURITY.md); do not restate it in the audit artifacts. +- Read [`AGENTS.md`](../../../AGENTS.md) before preparing a fix or regression; it owns the project's test form and commands. +- Leave `conf.debug_dissector` at its default value while establishing caller-visible behavior. +- During discovery, keep tracked source unchanged and use scratch harnesses for experiments. Do not retrieve candidate answers from vulnerability disclosures, advisories, issue reports, exploit writeups, prior audits, security patches, release notes, or repository history. Ordinary technical references such as protocol specifications and runtime semantics are allowed when needed to understand the supplied source. +- State the realistic caller, configuration, input source, and attacker capability before tracing a path. A direct call to an internal parser is not automatically a supported or attacker-reachable entry point. +- Write the security invariant before testing the suspected operation. Examples of invariant shapes include bounded work per wire-representable input, continued processing after malformed input, reply only after the required state transition, and acceptance only after the required authentication check. Treat these as hypotheses, not repository guarantees. +- Assume that at least one severe vulnerability exists so the search does not stop at easy negatives, but never lower the finding threshold or manufacture a result. + +At the first tool call, record the UTC start time in the campaign notes described below. + +## 2) Derive the attack-surface inventory + +Build the inventory from the code under audit: enumerate the real outer boundaries where untrusted bytes enter, then work inward through the transformations, validation, dispatch, state, and sensitive operations the implementation actually uses. Record each architecture-specific approach family in the coverage registry, including what was examined and how deeply, rather than beginning from a fixed component map. + +## 3) Create the canonical artifacts + +Use these default filenames throughout the campaign: + +- `SECURITY_AUDIT_NOTES.md`: architecture, trust boundaries, attack surfaces, approach-family and coverage registries, hypotheses, experiments, failed routes with reasons, unexplored areas, elapsed time, and checkpoint entries. +- `SECURITY_PRIMITIVES.md`: established or suspected primitives, each primitive's source, attacker control, constraints, confidence, possible consumers, and composition relationships. +- `SECURITY_AUDIT_CANDIDATES.md`: every serious result classified as Confirmed, Strong / validation incomplete, Unresolved, or Rejected, with concrete rejection reasons or exact missing evidence. +- `SECURITY_AUDIT_REPORT.md`: the reconciled final report. + +These names are defaults. The reader may rename or relocate them, but must choose all four canonical paths before spawning workers and use those paths consistently. Prefer an audit scratch directory outside the working tree's tracked files so the audit does not dirty the repository. + +Only the root agent may update the canonical artifacts. Workers must return evidence to the root instead of editing those files. They may create uniquely named scratch harnesses or reproducers outside tracked source, preferably in the audit scratch directory or a temporary directory. + +In the coverage registry, track each architecture-specific approach family as deeply examined, shallowly examined, or not examined. Use deeply examined only after tracing a realistic entry point through its defenses to the sensitive operation and checking important runtime assumptions. Reading a file or finding a suspicious operation is shallow coverage. + +In the primitive registry, treat a primitive as a bounded capability, not automatically as a finding. When combining primitives, trace the output of one into the input of the next and validate representation, state, lifetime, ordering, and every intervening defense. Never treat a required but unproven primitive as established. + +## 4) Orchestrate Codex workers + +The root agent exclusively controls campaign-level delegation and the canonical artifacts. Use parallel discovery only where assignments can proceed independently. + +Use the configured default subagent model and reasoning effort. Do not specify or override either setting in a spawn request. The current `spawn_agent` result and `list_agents` output do not report model or reasoning effort, so runtime confirmation is unavailable through these interfaces. If a future interface reports either value, stop the campaign and report a mismatch rather than continuing with a mixed worker pool. + +Call Codex's `list_agents` before every `spawn_agent` request. If no slot is available, call `wait_agent` and wait for a worker to complete; do not attempt or repeatedly retry another spawn. + +Discovery and redirected-discovery workers get no inherited root conversation or investigation history. In every Codex spawn request, set: + +```yaml +fork_turns: "none" +``` + +`fork_turns: "none"` is the available control for passing no surrounding conversation to the worker. The interface has no separate context toggle; do not add an unsupported context field. + +Do not include `model` or `reasoning_effort` in the request. Tell every worker that it must not spawn agents, and repeat that restriction in every new or follow-up assignment. Also tell workers not to edit canonical audit artifacts. + +Give each discovery worker only: + +- a bounded attack surface or vulnerability formulation; +- the repository-integrity and external-information constraints that apply to the run; +- the evidence standard and return contract below; +- instructions not to spawn agents or edit canonical audit artifacts. + +Never tell a discovery worker the root's leading hypothesis, favored findings, or other workers' results. This isolation applies during independent and redirected discovery so workers do not converge prematurely. + +Every worker must return all ten items: + +1. scope examined and hypotheses attempted; +2. exact affected code paths; +3. attacker-controlled input or influence; +4. relevant validation and defenses; +5. the complete path to the claimed security impact; +6. experimental evidence or counterexample; +7. exploitability constraints and deployment assumptions; +8. classification as Confirmed, Strong / validation incomplete, Unresolved, or Rejected; +9. confidence; +10. recommended next action. + +Vague status reports do not satisfy this contract. + +For adversarial validation, send a compact candidate packet containing: + +- the attacker model; +- the exact code path; +- the evidence; +- relevant defenses; +- exploitability constraints; +- open questions; +- an explicit task to falsify the candidate. + +Do not include unrelated campaign history. Assign validation to a worker that did not develop the candidate when capacity permits. + +## 5) Run three timed phases + +Use a 75-minute active investigation budget by default. The reader may scale all of these times for a different run, but must preserve the phases, synthesis checkpoints, final-reconciliation window, and no-idling rule. With the default budget, perform root synthesis checkpoints at approximately minutes 20, 40, and 60. Do not begin final reconciliation before minute 70, do not terminate before minute 75, and aim to finish by minute 85. + +### Phase 1: independent discovery + +Start a genuinely diverse portfolio across attack surfaces, trust boundaries, components, and vulnerability families. Preserve several incompatible hypotheses through the early campaign. Do not use a fixed component allocation, and do not share results among discovery workers. If workers cluster on one attractive surface, redirect available capacity toward materially different components, boundaries, or security ideas. + +### Phase 2: redirected discovery + +Use the coverage and disposition registries to move capacity away from crowded surfaces and over-investigated approach families toward unexamined components, materially different hypotheses, unresolved exploit conditions, and gaps exposed by falsified routes. Redirect a blocked approach by changing the attack surface, abstraction level, component, or vulnerability family. + +### Phase 3: adversarial validation and composition + +Challenge serious candidates, test whether independently established primitives compose, and run a final independent search outside the leading finding cluster. Validate every required link in a proposed chain. A candidate does not become stronger merely because several workers repeat the same unsupported assumption. + +At each checkpoint, perform this complete synthesis list: + +- reconcile every completed worker result into the disposition ledger; +- identify over-investigated and under-investigated families; +- record rejected hypotheses and the evidence that falsified them; +- update primitive, candidate, and coverage registries; +- redirect free capacity to the highest-value coverage gap or unresolved exploit condition; +- stop assigning general exploration to a mature finding unless a specific missing condition remains; +- record elapsed time and the checkpoint in `SECURITY_AUDIT_NOTES.md`. + +If the artifacts were renamed or relocated, record the checkpoint in the canonical campaign-notes file chosen at startup. + +Every serious worker result must be promoted, rejected with a concrete reason, or retained as Strong / validation incomplete or Unresolved with its missing evidence identified. It must not disappear when focus changes or context is compacted. + +Mark a route blocked when it depends on an unsupported assumption, unrealistic capability, unavailable primitive, or repeatedly unproven condition. Revisit it only when new code, evidence, or an established primitive can materially advance it. + +An unsuccessful hypothesis, a well-defended component, or completion of the first worker wave is a signal to redirect, not to finish. Do not finish early because a hypothesis failed or the first worker wave completed. Continue while time remains for underexplored coverage, adversarial validation, or exploit composition. Never sleep or idle merely to consume time. If a hard system limit prevents completion, preserve the current state and label the campaign incomplete. + +## 6) Apply a strict finding threshold + +A finding requires a realistic attacker capability and a complete, source-backed path from attacker influence, through the applicable validation and defenses, to a violated security invariant and meaningful impact. Establish important runtime and deployment assumptions experimentally when practical. + +Suspicious code alone is not a finding. Do not promote generic hardening, a dependency version concern without an applicable demonstrated vulnerability, unreachable unsafe behavior, an unvalidated parser anomaly, a speculative race, a crash without meaningful security consequence, or a claim requiring unrealistic attacker capability. Reject a path blocked by earlier authentication, authorization, validation, normalization, canonicalization, bounds checks, representation changes, or lack of attacker control. Do not treat an unproven primitive required by a proposed chain as established. + +Prefer one completely established result over multiple speculative ones, but continue checking distinct surfaces and hypothesis families after confirming a result. Finding count is not a coverage measure. If no result meets the threshold, say so directly; do not promote unresolved leads to fill the report. + +## 7) Prove behavior at the caller boundary + +### 7.1) Account for the `Raw` fallback + +Scapy deliberately catches dissector failures and may continue with `Raw`; [`SECURITY.md`](../../../SECURITY.md) states the policy and links to the implementation. With the default `conf.debug_dissector`, an inner failure that lands in `Raw` and remains contained is not a security finding. A finding must show that the security-relevant failure escapes the fallback and reaches the caller; a harness that calls only an inner parser has not established that. + +For a candidate based on a dissector exception or stopped parsing: + +1. exercise the normal outer entry point with the default configuration; +2. identify every applicable exception boundary between the failing operation and caller; +3. inspect the returned packet for `Raw` at the failed layer; +4. place a benign packet after the trigger and observe whether the same reader, receive loop, or sniffer continues; +5. promote only if the security-relevant failure escapes containment and is observable at the caller boundary. + +The same principle applies to hangs and excessive work: measure the effect at the outer boundary rather than treating an expensive internal call as proof of reachable denial of service. + +### 7.2) Require a negative control + +Run the trigger and a negative control through the same outer entry point, configuration, initial state, and surrounding packet exchange. The negative control must remove or neutralize only the proposed trigger and show the claimed boundary holding. For stateful behavior, begin both runs from fresh equivalent state. + +If the trigger and control both fail, both succeed, or never reach the claimed boundary, the experiment does not establish the defect. Record that result rather than interpreting the trigger run alone. + +## 8) Establish and falsify every serious candidate + +For each candidate, establish all applicable items: + +1. attacker capability and exact controlled input, timing, ordering, or state; +2. concrete outer entry point and complete data- or control-flow path; +3. relevant authentication, authorization, validation, normalization, canonicalization, bounds, representation, and fallback defenses; +4. vulnerable operation and the violated security invariant; +5. resulting primitive and realistic confidentiality, integrity, or availability impact; +6. wire, runtime, configuration, deployment, mitigation, and exploitability constraints; +7. a minimal reproduction with deterministic observations and a negative control. + +Use source inspection, existing tests, targeted harnesses, crafted inputs, tracing, debugging, instrumentation, fuzzing, and local instances where useful. Inspect the actual relevant third-party implementation when its behavior is security-critical and locally available. Unsupported statements that exploitation is likely, obvious, or straightforward do not close a missing link. + +Then perform a separate falsification pass. Start from the claimed invariant and attempt to break the candidate with counterexamples: challenge reachability and attacker control, look for an earlier defense or alternate dispatch, verify default configuration, bound the input to a wire-representable form, check state, lifetime, integer, parser, cryptographic, privilege, and concurrency assumptions, and try to make the negative control indistinguishable from the trigger. Record the evidence whether the candidate survives or fails. Every High or Critical candidate must survive an independent attempt to falsify it. + +## 9) Write the regression in Scapy's form + +When the requested work includes fixing a confirmed defect, follow the project's regression form and verification commands in [`AGENTS.md`](../../../AGENTS.md). + +The regression must drive the affected outer behavior when practical, include the triggering case and its negative control, and assert the security boundary rather than only the absence of an exception. Keep a smaller parser-level case only when it isolates the root cause in addition to the boundary-level regression. + +## 10) Reconcile and report + +Before returning, ensure no worker remains live, reconcile every worker result, and rerun representative reproducers when practical. `SECURITY_AUDIT_REPORT.md`, or the canonical report path chosen at startup, must include for every confirmed vulnerability: + +1. title, severity, confidence, and attacker model; +2. affected code and root cause; +3. complete attack path from attacker influence to boundary violation; +4. precise exploit primitive and resulting security impact; +5. existing defenses and why they fail or constrain exploitation; +6. concrete reproduction, observations, and negative control; +7. deployment assumptions, mitigations, and exploitability limitations; +8. composition opportunities, without claiming unproven links. + +Conclude with `Audit Coverage`, describing components, attack surfaces, approach families, depth of coverage, significant disproven hypotheses, and substantially unexplored areas. Then include `Highest-Value Unresolved Leads`, preserving the strongest hypotheses that still lack required evidence and naming the next experiment for each. + +Operate autonomously. Preserve search diversity, resist premature convergence, falsify promising findings, redirect blocked work, and test whether modest primitives compose. Optimize for the most severe real vulnerability the source contains, not the number of report entries. Begin now. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..a1617b7784c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# Scapy checkout instructions for agents + +[`CONTRIBUTING.md`](CONTRIBUTING.md) is Scapy's contribution guide; this file is its operational companion for agents working in this checkout, not a replacement. + +## Put code in the right place + +A layer is a `Packet` subclass with a `fields_desc` list. For packet, field, +binding, and layer-test patterns, use +[`scapy-packet-fields`](.github/skills/scapy-packet-fields/SKILL.md). + +- New protocols may go in either location: `scapy/layers/` normally contains + protocols found on common networks, while `scapy/contrib/` normally contains + uncommon or specific protocols. +- Code in `scapy/layers/` should not import `scapy/contrib/`. Contrib code may + import from either location. Contrib modules are loaded with `load_contrib()` + rather than by the default layer loader. +- Implement protocol-related features in the module containing that protocol. + Other features may live in `scapy/modules/` or `scapy/contrib/`. +- Be especially careful about CPU and memory costs in Scapy core code such as + `scapy/packet.py`; packet initialization is a hot path. + +## Add UTScapy tests + +Follow [`CONTRIBUTING.md`'s test requirements](CONTRIBUTING.md#tests). Scapy's +test suite uses UTScapy `.uts` campaigns under `test/`. + +A campaign has this form: + +```text +% Packet regression ++ Build and dissect += Round trip +~ protocol_keyword +packet = Protocol(field=value) +decoded = Protocol(bytes(packet)) +decoded.field == value +``` + +`%`, `+`, and `=` start a campaign, test set, and unit test. `~` assigns +keywords. Unprefixed lines inside a unit test are Python; the truth value of the +last expression decides the result. + +Run the affected campaign directly while developing, for example: + +```console +./test/run_tests -t test/contrib/automotive/autosar/pdu.uts -K tshark -N +``` + +Run the portable suite from the repository root with Python and `tox` +installed: + +```console +./test/run_tests +``` + +## Check source changes + +Follow [`CONTRIBUTING.md`'s coding style and conventions](CONTRIBUTING.md#coding-style--conventions). +Flake8 checks `scapy/` with an 88-column limit and the per-file exceptions in +`tox.ini`. CI invokes the lint and type-check environments under Python 3.12. +Because `tox.ini` does not select that interpreter for these environments, +install tox for Python 3.12 and invoke it explicitly to match CI: + +```console +python3.12 -m tox -e flake8 +python3.12 -m tox -e mypy +``` + +The mypy environment runs both Linux and Windows configurations. + +## Do not hand-edit generated content + +- Do not modify files in `scapy/layers/msrpce/raw/` whose header says they are + generated by `midl-to-scapy`. +- Update the encoded data in `scapy/libs/bluetoothids.py`, + `scapy/libs/manuf.py`, and `scapy/libs/ethertypes.py` through their matching + `scapy/tools/generate_*.py` scripts. + +## Prepare commits and pull requests + +Follow [`CONTRIBUTING.md`'s guidance on AI-assisted reports and PRs](CONTRIBUTING.md#ai-assisted-reports-and-prs) +and [submitting pull requests](CONTRIBUTING.md#submitting-pull-requests). + +## Potential security bugs + +If a change uncovers something that may be a security bug, follow +[`SECURITY.md`](SECURITY.md) for classification and reporting. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4be9fc68ae8..de06d338c37 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,10 @@ If you use AI tools to help find or draft a bug report (or a PR), please mention Purely AI-generated or overly verbose reports are not supported and might be closed. A quick human check keeps triage efficient for everyone. +For a report, verify the bug against the current development code. For a PR, follow [Coding style & conventions](#coding-style--conventions) and [Tests](#tests): add tests (or explain why they aren't relevant), and check that every commit has the required `AI-Assisted` tag. To run the optional flake8 check with the same Python version as CI, install tox for Python 3.12 and use `python3.12 -m tox -e flake8`; `tox.ini` does not select that interpreter for this environment. + +If you're configuring an agent for Scapy, see [AGENTS.md](AGENTS.md). + ### Enhancements / feature requests If you want a feature in Scapy, but cannot implement it yourself or diff --git a/SECURITY.md b/SECURITY.md index 71097d4267a..6d05f565310 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,6 +3,8 @@ We take security seriously. If you find a critical bug report, please reach out privately using [Github's security tab](https://github.com/secdev/scapy/security). Please note however that we only provide support for the **latest** version of Scapy (master). +Please include a minimal working example against an unmodified checkout, plus a negative control where the same exchange works without the triggering packet. + ### Examples of what counts as a vulnerability in Scapy * a DoS during parsing: provide a packet that triggers an infinite loop when parsing (high issue), or takes a quadratic amount of resources (low issue) @@ -14,6 +16,6 @@ Please note however that we only provide support for the **latest** version of S * a packet that crashes a dissector and fallbacks to being parsed as `Raw`: we have a [conservative approach](https://github.com/secdev/scapy/blob/caa005dae9bddeb93b11286327149c824db7732a/scapy/supersocket.py#L218-L227) with dissector failures where a crash doesn't stop the parsing thread. This is not a security issue, but is still a bug so please open a public issue ! * a DoS that is triggered by dissecting a packet that is too large to exist on the wire (e.g. packet larger than the MTU) -* a DoS that is triggered when crafting a packet (and that is not a response to a stimulus, e.g. answering machines, automatons, etc.) +* a DoS that is triggered when crafting a packet, unless the packet is crafted in response to a stimulus (e.g. answering machines, automatons, etc.) * having `conf.debug_dissector != 0` * anything that requires changing Scapy's configuration or caching files, including but not limited to `.config/scapy/*`, `.cache/scapy/*`