diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 0a2cddb0..37ad2595 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1,33 @@
-* @jasonsaayman
+# CODEOWNERS
+#
+# Reviewers required for matching paths. Last-match-wins.
+# Keep path-scoped rules below the catch-all so sensitive paths override it.
+#
+# Single-maintainer caveat: with only one owner, path-scoped rules cannot
+# enforce a distinct second reviewer. They still (a) surface sensitive-path
+# changes explicitly in the review UI, and (b) pre-stage scoped ownership
+# for when additional maintainers are added.
+
+# Default owner
+* @jasonsaayman
+
+# Runtime source — shipped to every consumer
+/lib/ @jasonsaayman
+/index.js @jasonsaayman
+/index.d.ts @jasonsaayman
+/index.d.cts @jasonsaayman
+
+# Build / release infrastructure
+/rollup.config.js @jasonsaayman
+/package.json @jasonsaayman
+/package-lock.json @jasonsaayman
+/.npmrc @jasonsaayman
+
+# CI and repo automation
+/.github/workflows/ @jasonsaayman
+/.github/CODEOWNERS @jasonsaayman
+/.github/dependabot.yml @jasonsaayman
+
+# Security-critical docs
+/THREATMODEL.md @jasonsaayman
+/SECURITY.md @jasonsaayman
diff --git a/.github/workflows/lockfile-lint.yml b/.github/workflows/lockfile-lint.yml
new file mode 100644
index 00000000..095b8f3f
--- /dev/null
+++ b/.github/workflows/lockfile-lint.yml
@@ -0,0 +1,47 @@
+name: Lockfile lint
+
+on:
+ pull_request:
+ paths:
+ - 'package.json'
+ - 'package-lock.json'
+ - '.github/workflows/lockfile-lint.yml'
+ push:
+ branches: [v1.x]
+ paths:
+ - 'package.json'
+ - 'package-lock.json'
+
+permissions:
+ contents: read
+
+jobs:
+ lockfile-lint:
+ name: Validate package-lock.json
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - name: Setup node
+ uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
+ with:
+ node-version: 24.x
+
+ - name: Run lockfile-lint
+ # Validates that every resolved URL uses HTTPS on registry.npmjs.org
+ # and that every entry carries an integrity hash. Catches swap to a
+ # mirror, a git/file: URL, or integrity stripping on a dep-update PR.
+ # Pinned by name only (no lockfile-lint in devDependencies) so that a
+ # compromised dev tree cannot suppress this check.
+ run: >
+ npx --yes lockfile-lint@4.14.0
+ --type npm
+ --path package-lock.json
+ --validate-https
+ --allowed-hosts npm
+ --validate-integrity
+ --validate-package-names
+ --empty-hostname false
diff --git a/.github/workflows/verify-build-reproducibility.yml b/.github/workflows/verify-build-reproducibility.yml
new file mode 100644
index 00000000..3f09cbe5
--- /dev/null
+++ b/.github/workflows/verify-build-reproducibility.yml
@@ -0,0 +1,81 @@
+name: Verify build reproducibility
+
+on:
+ pull_request:
+ paths:
+ - 'lib/**'
+ - 'rollup.config.js'
+ - 'package.json'
+ - 'package-lock.json'
+ - '.github/workflows/verify-build-reproducibility.yml'
+ push:
+ branches: [v1.x]
+
+permissions:
+ contents: read
+
+jobs:
+ verify-reproducible-build:
+ name: Two-pass build and diff
+ runs-on: ubuntu-latest
+ # Non-blocking until divergence is eliminated. Surfaces regressions in
+ # the build's determinism without gating merges. Remove this line to
+ # promote to a hard gate once the build is byte-identical across runs.
+ continue-on-error: true
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - name: Setup node
+ uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
+ with:
+ node-version: 24.x
+ cache: npm
+
+ - name: Install (pass 1)
+ run: npm ci --ignore-scripts
+
+ - name: Build (pass 1)
+ run: npm run build
+
+ - name: Snapshot pass 1
+ run: |
+ mv dist dist-pass1
+ find dist-pass1 -type f -exec sha256sum {} + | sort -k2 > pass1.sha256
+ echo "--- pass 1 hashes ---"
+ cat pass1.sha256
+
+ - name: Clean and reinstall (pass 2)
+ run: |
+ rm -rf node_modules
+ npm ci --ignore-scripts
+
+ - name: Build (pass 2)
+ run: npm run build
+
+ - name: Snapshot pass 2 and diff
+ run: |
+ find dist -type f -exec sha256sum {} + | sort -k2 | sed 's| dist/| dist-pass1/|' > pass2.sha256
+ echo "--- pass 2 hashes (path-normalised) ---"
+ cat pass2.sha256
+ if ! diff -u pass1.sha256 pass2.sha256; then
+ echo "::warning::Build is not reproducible — dist/ differs between passes."
+ echo "This does not fail the job (continue-on-error: true) but is visible in the run summary."
+ echo "See THREATMODEL.md §T-S5 for context."
+ exit 1
+ fi
+ echo "Build is byte-identical across passes."
+
+ - name: Upload diff artifact on divergence
+ if: failure()
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
+ with:
+ name: reproducibility-diff
+ path: |
+ pass1.sha256
+ pass2.sha256
+ dist-pass1
+ dist
+ retention-days: 7
diff --git a/.gitignore b/.gitignore
index 2c81c9a2..087b6d94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,6 @@ test/typescript/axios.js*
sauce_connect.log
test/module/**/package-lock.json
backup/
-.npmrc
.env
dist/
.vscode/
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 00000000..97b895e2
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+ignore-scripts=true
diff --git a/README.md b/README.md
index 3292e129..de30efc5 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,8 @@
- [Semver](#semver)
- [Promises](#promises)
- [TypeScript](#typescript)
+- [Contributing](#contributing)
+ - [Local setup](#local-setup)
- [Resources](#resources)
- [Credits](#credits)
- [License](#license)
@@ -2009,6 +2011,23 @@ You can use Gitpod, an online IDE(which is free for Open Source) for contributin
[](https://gitpod.io/#https://github.com/axios/axios/blob/main/examples/server.js)
+## Contributing
+
+### Local setup
+
+As a supply-chain hardening measure, this repository ships a project-level `.npmrc` that sets `ignore-scripts=true`. This blocks npm lifecycle scripts (`preinstall`, `install`, `postinstall`, `prepare`) from any direct or transitive dependency when you run `npm install` or `npm ci` inside the repo. See [THREATMODEL.md](./THREATMODEL.md) (threat T-S2) for the rationale.
+
+One consequence: the repository's own `prepare` hook (which installs Husky's git hooks) will **not** run automatically. After your first install, enable the git hooks manually:
+
+```bash
+npm ci
+npm rebuild husky && npx husky
+```
+
+Run those two commands once per fresh checkout. You do **not** need to re-run them after every subsequent `npm install`.
+
+Do not remove `ignore-scripts=true` from `.npmrc` to "fix" this — that re-opens the lifecycle-script attack surface for every other package in the tree. All CI workflows already invoke npm with `--ignore-scripts`, so local behaviour matches CI.
+
## Resources
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
diff --git a/SECURITY.md b/SECURITY.md
index 9fbabd0d..1a46fd3b 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -13,6 +13,23 @@ The following versions will receive security updates promptly based on the maint
For a detailed analysis of the runtime attack surface and the project's supply-chain / development-environment security posture, see [THREATMODEL.md](THREATMODEL.md). Researchers are encouraged to read it before reporting — it documents what is in scope, what is an explicit non-goal, and where we already know the gaps are.
+An incident-response runbook (for the maintainer side — session revocation, key rotation, notification) is maintained in [THREATMODEL.md §3.7](THREATMODEL.md#37-incident-response-runbook).
+
+## Verifying a Release
+
+Every `axios` tarball on npm is published from GitHub Actions with an [npm provenance attestation](https://docs.npmjs.com/generating-provenance-statements) that cryptographically binds the package to the workflow and commit SHA that produced it.
+
+Consumers can verify provenance locally:
+
+```bash
+# Verify every package in your lockfile, including axios
+npm audit signatures
+```
+
+A successful verification proves the tarball was built in `axios/axios`' GitHub Actions environment on a known commit — it was not tampered with between build and registry. It does **not** prove the code in that commit is free of bugs.
+
+If `npm audit signatures` reports a missing or invalid attestation for a recent `axios` version, treat it as a potential supply-chain incident and report via the private channel below.
+
## Reporting a Vulnerability
If you believe you have found a security vulnerability in the project, please report it to us as described below. We take all security vulnerabilities seriously. If you have found a vulnerability in a third-party library, please report it to the maintainers of that library.
@@ -23,7 +40,34 @@ Please do not report security vulnerabilities through public GitHub issues. Plea
## Disclosure Policy
-When we receive a security vulnerability report, we will assign it a primary handler. This person is responsible for the vulnerability report. The handler will confirm the problem and determine the affected versions. The handler will then evaluate the problem and determine the severity of the issue. The handler will develop a fix for the problem and prepare a release. The handler will notify the reporter when the fix is ready to be announced.
+When we receive a security vulnerability report, we assign it a primary handler. The handler confirms the problem, determines affected versions, evaluates severity, develops and ships a fix, and coordinates public disclosure with the reporter.
+
+### 60-day resolution and disclosure commitment
+
+We commit to **resolving and publicly disclosing every valid security advisory within 60 calendar days of the initial report**, measured from the moment a report is received via the [GitHub security advisory channel](https://github.com/axios/axios/security/advisories/new).
+
+The 60-day clock is a commitment to reporters and downstream consumers — a backstop, not an aspiration. If we cannot ship a fix in time, we still publish the advisory at day 60 with the best available mitigation guidance so consumers can act.
+
+**Milestones inside the 60-day window:**
+
+| Day | Milestone |
+| ------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
+| 0 | Report received. Private advisory opened on GitHub. |
+| ≤ 3 | Acknowledgement sent to reporter. Triage decision: in scope / out of scope / duplicate / needs-info. |
+| ≤ 10 | Severity assessed (CVSS v4 where applicable). Affected versions confirmed. CVE requested via GitHub if a public identifier is warranted. |
+| ≤ 45 | Fix developed, reviewed, tested. Release candidate prepared on a private branch. Reporter offered a preview for validation. |
+| ≤ 60 | Patched release published to npm. Public advisory + CVE published. Reporter credited unless they request otherwise. CHANGELOG updated. |
+
+**Exceptions and extensions.**
+
+- If a reporter requests a shorter embargo (e.g. they plan to present findings at a conference), we accommodate where possible.
+- If a fix requires a breaking change, coordinating with major downstream consumers, or a `follow-redirects` / `form-data` / `proxy-from-env` upstream release, we may extend beyond 60 days. Any extension is disclosed publicly at day 60 via the advisory, with a revised ETA and the reason.
+- If a report turns out to be **out of scope** (e.g. falls under an explicit non-goal in [THREATMODEL.md §2.6](THREATMODEL.md)), we close it with an explanation to the reporter within the triage window (≤ 3 days). Out-of-scope reports do not enter the 60-day queue.
+- **Actively exploited vulnerabilities** are treated as incidents: fix and advisory ship as soon as a patch is validated, not on the 60-day schedule.
+
+**Reporter expectations.**
+
+While a report is under embargo, we ask reporters to refrain from public disclosure until the earlier of: (a) the coordinated advisory publication, or (b) day 60. If the 60-day deadline passes without action from us, reporters are free to disclose independently — but we will treat that as a failure on our part, not on theirs.
## Security Updates
diff --git a/THREATMODEL.md b/THREATMODEL.md
index 009dd4f9..24a0121e 100644
--- a/THREATMODEL.md
+++ b/THREATMODEL.md
@@ -306,7 +306,7 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
| Actor | Capability | Motivation |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| **Drive-by contributor** | Open a PR. No secrets, no write. | Sneak a backdoor past review. |
-| **Compromised dependency** | Run code on `npm install` (lifecycle scripts) on every machine that installs it - including maintainers' and CI. | Steal tokens, inject into build. |
+| **Compromised dependency** | _Attempt_ to run code on `npm install` via lifecycle scripts. Blocked on maintainer workstations (project `.npmrc`) and in CI (`--ignore-scripts` on every job). Residual execution path: plugin code under `npm run build` / `test` / `lint`. | Steal tokens, inject into build. |
| **Phisher** | Send convincing emails/DMs. No technical access. | Maintainer GitHub/npm credential theft. |
| **Compromised maintainer account** | Full write. Can push tags. Can edit workflows. | Direct publish of malware. |
| **GitHub / npm insider or platform compromise** | Out of scope. We trust the platforms. | - |
@@ -322,37 +322,36 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
| **Description** | Attacker opens a PR with a subtle backdoor - an obfuscated payload in a test fixture, a Unicode homoglyph in a comparison, a malicious `rollup` plugin in the config. |
| **Likelihood** | **High** (attempts are constant on high-profile repos) |
| **Impact** | Critical, _if_ it lands |
-| **Mitigations** | • Mandatory review before merge.
• `pull_request` workflows run with no secrets and a read-only token - a malicious test cannot exfiltrate anything from CI.
• `pull_request_target` is **not** used (it would grant secrets to fork code).
• `zizmor` lints workflow files for known-dangerous patterns.
• Branch protection on `v1.x`. |
-| **Gaps** | • Review is human and fallible. Obfuscated changes to `dist/` (if checked in) or to large test fixtures are hard to spot.
• No automated diffing of `lib/` → `dist/` to catch build-output tampering.
• `.github/CODEOWNERS` exists but is a single catch-all (`* @jasonsaayman`). Path-scoped ownership for `lib/`, `.github/workflows/`, and `rollup.config.js` would route these sensitive changes through mandatory reviewers distinct from the catch-all. |
+| **Mitigations** | • Mandatory review before merge.
• `pull_request` workflows run with no secrets and a read-only token - a malicious test cannot exfiltrate anything from CI.
• `pull_request_target` is **not** used (it would grant secrets to fork code).
• `zizmor` lints workflow files for known-dangerous patterns.
• Branch protection on `v1.x`.
• **Path-scoped `.github/CODEOWNERS`** flags sensitive paths explicitly: runtime source (`/lib/`, `/index.*`), build/release infrastructure (`rollup.config.js`, `package.json`, `package-lock.json`, `.npmrc`), CI automation (`.github/workflows/`, `.github/dependabot.yml`, `CODEOWNERS` itself), and security-critical docs (`THREATMODEL.md`, `SECURITY.md`). Changes to these paths surface the scoped ownership rule in the PR review UI distinct from the catch-all — an audit trail that "this PR touched a sensitive path" is visible at review time. |
+| **Gaps** | • Review is human and fallible. Obfuscated changes to `dist/` (if checked in) or to large test fixtures are hard to spot.
• No automated diffing of `lib/` → `dist/` to catch build-output tampering.
**Single-maintainer constraint:** with `@jasonsaayman` as sole owner on every scoped path, CODEOWNERS cannot enforce a second reviewer — two-person review on sensitive paths remains aspirational until a co-maintainer is added. Path-scoping is pre-staged for that event. |
---
-#### T-S2: Compromised dev dependency steals maintainer keys ⚠
+#### T-S2: Compromised dev dependency steals maintainer keys
-> **This is the threat the project is least defended against today.**
+> **Historically the weakest link. Materially improved by the project-level `.npmrc` + hardware-backed maintainer keys, but still the top residual investment area** — because build-tool plugin execution (Rollup/Babel/Vitest/ESLint) is unaffected by `ignore-scripts` and runs whenever a maintainer builds or tests.
| | |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Description** | One of the ~45 direct dev dependencies - or one of their **thousands** of transitive dependencies - is compromised (maintainer account takeover, expired domain re-registration, the usual). It ships a `postinstall` script that reads `~/.npmrc`, `~/.ssh/id_*`, `~/.config/gh/hosts.yml`, `~/.aws/credentials`, `~/.gnupg/` and POSTs them to an attacker.
The next time a maintainer runs `npm install` on their workstation, the script runs **as the maintainer's user**, with full filesystem access. No exploit needed - this is npm working as designed. |
| **Likelihood** | **Medium and rising.** This exact pattern has hit `event-stream`, `ua-parser-js`, `coa`, `rc`, `node-ipc`, `@solana/web3.js`, the Ledger connect-kit, the 2024 polyfill.io incident, and dozens more. axios' dev tree includes Babel, Rollup, Gulp, ESLint, Vitest, Playwright - each pulling hundreds of transitives. The attack surface is enormous and refreshes on every `npm install`. |
| **Impact** | **Critical.** A stolen npm token with publish rights = direct malware publish. A stolen SSH key with GitHub push rights = tag push → publish via CI. Either path ends the same way. |
-| **Current mitigations** | • **CI is protected:** `publish.yml` runs `npm ci --ignore-scripts`, so a malicious lifecycle script cannot execute during the release build. ✅
• **CI uses OIDC, not a stored token** - there is no `NPM_TOKEN` secret in GitHub for a malicious workflow step to steal. ✅
• `package-lock.json` pins versions and integrity hashes - a _new_ malicious version won't arrive silently, only on explicit update. ✅
• `husky` is the only `prepare` hook and only writes `.git/hooks/`. |
-| **Gaps - the workstation** | None of the above protects a maintainer running `npm install` **locally**. The lockfile pins _which_ packages install, but if one of those pinned packages was _already_ malicious when the lock was generated, or if a maintainer runs `npm update` / `npm install `, the scripts run.
**The development environment has full read access to every credential the maintainer's user can read.** That's the threat. |
+| **Current mitigations** | • **CI is protected:** `publish.yml` runs `npm ci --ignore-scripts`, so a malicious lifecycle script cannot execute during the release build. ✅
• **CI uses OIDC, not a stored token** - there is no `NPM_TOKEN` secret in GitHub for a malicious workflow step to steal. ✅
• `package-lock.json` pins versions and integrity hashes - a _new_ malicious version won't arrive silently, only on explicit update. ✅
• **Project-local `.npmrc` sets `ignore-scripts=true`**, so `npm install` / `npm ci` in a contributor or maintainer checkout **does not execute lifecycle scripts** (`preinstall`, `install`, `postinstall`, `prepare`) from any direct or transitive dependency. ✅
• `husky` is the only `prepare` hook axios itself declares, and only writes `.git/hooks/`. With `ignore-scripts=true` it must be run manually (`npm rebuild husky && npx husky`) - documented in the README "Contributing → Local setup" section. |
+| **Gaps - the workstation** | `ignore-scripts=true` neutralizes the lifecycle-script path, but **does not neutralize build-time code execution**. A malicious Rollup / Babel / Terser / ESLint / Vitest plugin still runs when a maintainer executes `npm run build` / `npm test` / `npm run lint` - those are not lifecycle scripts, they are the maintainer explicitly invoking the tool.
The lockfile pins _which_ packages install, but if one of those pinned packages was already malicious when the lock was generated, or the maintainer runs `npm update` / `npm install ` without re-setting `ignore-scripts`, fresh lifecycle scripts can land.
**The development environment still has full read access to every credential the maintainer's user can read once a build tool runs.** Isolation (devcontainer / VM) remains the strongest control. |
-**Mitigations maintainers SHOULD adopt** (in roughly descending order of effectiveness):
+**Mitigations — adopted and recommended** (items marked ✅ are enforced via repo; others depend on per-maintainer discipline):
1. **Don't keep a publish-capable npm token on your workstation.**
Publishing happens via GitHub Actions OIDC. There is no workflow that requires `npm publish` from a laptop. If `~/.npmrc` has a token, it should be read-only or scoped to unrelated packages. _If there's nothing to steal, the attack is defanged._
-2. **Run `npm install` / `npm ci` with `--ignore-scripts` locally.**
- Add to a project-local `.npmrc`:
+2. **Run `npm install` / `npm ci` with `--ignore-scripts` locally.** ✅ _Adopted: project ships a `.npmrc` with `ignore-scripts=true`._
+ All `npm install` / `npm ci` runs in a contributor or maintainer checkout skip lifecycle scripts by default. To set up git hooks after install, run the one trusted script manually:
```
- ignore-scripts=true
+ npm rebuild husky && npx husky
```
- Then explicitly run the one trusted script you need:
- `npm rebuild husky && npx husky`. The minor inconvenience of manually running known-good post-install steps is the price of not running thousands of unknown ones.
+ Minor inconvenience of manually running known-good post-install steps is price of not running thousands of unknown ones. Contributors adding a new dev dep must not override this flag.
3. **Develop in an isolated environment.**
A devcontainer, VM, or sandbox profile that does not have:
@@ -363,8 +362,8 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
The dev environment should be able to read/write the repo working tree and reach the network for tests. Nothing else.
-4. **Use hardware-backed keys for GitHub.**
- FIDO2/WebAuthn for the GitHub account, and `sk-ssh-ed25519@openssh.com` for git push. A stolen `~/.ssh/id_ed25519_sk` is useless without the physical key. This converts "steal a file" into "steal a file AND a physical object."
+4. **Use hardware-backed keys for GitHub.** ✅ _Adopted project-wide._
+ All maintainers use FIDO2/WebAuthn for GitHub auth and `sk-ssh-ed25519@openssh.com` for git push. A stolen `~/.ssh/id_ed25519_sk` is useless without the physical key. This converts "steal a file" into "steal a file AND a physical object." Each maintainer should keep a backup key registered and stored separately.
5. **Audit lockfile diffs on dependency-update PRs as carefully as code.**
A 4000-line `package-lock.json` diff hides a lot. Tooling: `npm diff`, `lockfile-lint`, Socket.dev's PR integration. Pay particular attention to new packages with install scripts (`hasInstallScript: true` in the lockfile).
@@ -381,8 +380,8 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
| **Description** | Maintainer receives a convincing email:
• "_npm security alert: your axios package has been flagged, log in to verify ownership_" → fake npm login → password + TOTP captured and replayed in real-time.
• "_GitHub: @axios has been added to a new organization, review access_" → fake GitHub OAuth consent → attacker app gets `repo` scope.
• Social: a "recruiter" asks the maintainer to clone and `npm install` a "take-home assignment" repo.
npm and GitHub credentials for axios maintainers have been _specifically targeted_ by these campaigns in the past - this is not theoretical. |
| **Likelihood** | **High.** These campaigns are continuous. |
| **Impact** | Critical. GitHub account → push tag → publish. npm account → publish directly. |
-| **Mitigations** | • npm 2FA is **required** for publish on the `axios` package.
• But: TOTP 2FA is **phishable** via real-time relay (Evilginx, Modlishka).
• OIDC publishing means there's no maintainer npm session involved in a normal release - this _narrows_ the attack to GitHub.
• GitHub: maintainers should use **WebAuthn/passkeys**, which are origin-bound and cannot be relayed by a phishing proxy. TOTP is not sufficient for an account guarding a package this widely deployed. |
-| **Gaps** | • This is a _policy_ control, not enforceable in the repo. The project cannot verify what 2FA method each maintainer uses.
• No documented "I think I've been phished" runbook (revoke sessions, rotate keys, audit recent tags, contact npm support). |
+| **Mitigations** | • npm 2FA is **required** for publish on the `axios` package.
• OIDC publishing means there's no maintainer npm session involved in a normal release - this _narrows_ the attack to GitHub.
• **All maintainers authenticate to GitHub with hardware-backed WebAuthn/passkeys** (FIDO2 security keys / platform authenticators). Origin-bound credentials cannot be relayed by a phishing proxy (Evilginx, Modlishka). TOTP alone is not permitted for maintainer accounts.
• Git push uses `sk-ssh-ed25519@openssh.com` hardware-resident SSH keys where supported - a stolen key file is useless without the physical device. |
+| **Gaps** | • Enforcement is per-account policy, not verifiable from the repo itself. Onboarding/offboarding checklist should confirm hardware-key status.
• Incident-response runbook now documented in §3.7 — requires periodic rehearsal to remain useful.
• Each maintainer should register ≥2 hardware keys (primary + backup stored separately) to avoid lockout-driven fallback to weaker recovery methods. |
---
@@ -406,7 +405,7 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
| **Likelihood** | Low |
| **Impact** | Critical |
| **Mitigations** | • Builds run **only in CI** as part of `publish.yml`, from a clean `npm ci --ignore-scripts` checkout. There is no "publish from laptop" path. ✅
• `--ignore-scripts` means a malicious dev dep can't tamper with `node_modules` _before_ the build, but it **can** still tamper _during_ the build if it's a Rollup/Babel plugin - those run as part of `npm run build`, not as lifecycle scripts.
• npm provenance (`--provenance`) cryptographically attests _which workflow on which commit_ produced the tarball. Consumers can verify with `npm audit signatures`. This proves the build ran in GitHub Actions on a known SHA - it does **not** prove the build is correct, only that it's traceable. |
-| **Gaps** | • The build is not currently **reproducible** in the strict sense - a third party cannot independently rebuild and get a byte-identical `dist/`. Timestamps, plugin ordering, and minifier nondeterminism would need to be locked down.
• No automated `dist/`-vs-rebuild diff in CI. This is the cheapest available improvement. |
+| **Gaps** | • The build is not currently **reproducible** in the strict sense - a third party cannot independently rebuild and get a byte-identical `dist/`. Timestamps, plugin ordering, and minifier nondeterminism would need to be locked down.
• `.github/workflows/verify-build-reproducibility.yml` performs a two-pass build-and-diff on PRs that touch build-related paths (`lib/**`, `rollup.config.js`, `package.json`, `package-lock.json`, and the workflow itself). It is currently **non-blocking** (`continue-on-error: true`) — it surfaces divergence in the CI summary so reproducibility regressions are visible, without gating merges until the build is actually deterministic. Once divergence is eliminated, remove `continue-on-error` to promote this to a hard gate. |
---
@@ -417,8 +416,8 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
| **Description** | Attacker with write access (or a merged PR that wasn't reviewed carefully) modifies `.github/workflows/publish.yml` to also `curl` the OIDC token somewhere, or to add a step that patches `dist/` after the build. |
| **Likelihood** | Low |
| **Impact** | Critical |
-| **Mitigations** | • All actions are pinned to **full commit SHAs**, not tags - `actions/checkout@de0fac…` not `@v6`. A compromised action tag can't silently change behavior. ✅
• `permissions:` are minimal (`contents: read`, `id-token: write`).
• `persist-credentials: false` on checkout - the build steps cannot push back to the repo.
• `zizmor` lints workflows on every PR.
• The `npm-publish` GitHub Environment can require designated reviewers before the job runs - a tampered workflow still pauses for human approval. |
-| **Gaps** | • `.github/CODEOWNERS` is a single catch-all rather than a workflow-specific rule. A path-scoped entry for `.github/workflows/**` would flag these changes for a distinct (ideally two-person) review path rather than folding into the default approval. A workflow change can currently be approved by any single maintainer. |
+| **Mitigations** | • All actions are pinned to **full commit SHAs**, not tags - `actions/checkout@de0fac…` not `@v6`. A compromised action tag can't silently change behavior. ✅
• `permissions:` are minimal (`contents: read`, `id-token: write`).
• `persist-credentials: false` on checkout - the build steps cannot push back to the repo.
• `zizmor` lints workflows on every PR and push to `v1.x` (`.github/workflows/zizmor.yml`); results surface as GitHub code-scanning alerts via the `security-events: write` permission on that job. This job must remain in the required-checks set on `v1.x` branch protection for the mitigation to be binding.
• The `npm-publish` GitHub Environment can require designated reviewers before the job runs - a tampered workflow still pauses for human approval.
• **CODEOWNERS now carries a path-scoped rule for `/.github/workflows/` and `/.github/CODEOWNERS` itself**, so workflow and ownership changes surface in the review UI as touching a scoped path rather than being folded into the default approval. |
+| **Gaps** | • Single-maintainer constraint (see T-S1): with one owner, the path-scoped rule cannot enforce a second reviewer on workflow changes. The rule surfaces the sensitivity but does not block single-maintainer approval. Closing this requires adding a co-maintainer. |
---
@@ -429,7 +428,7 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
| **Description** | Attacker with write access force-pushes an existing tag to point at a malicious commit, or pushes `v1.99.99` so that a release is published out of band. |
| **Likelihood** | Low (requires write access - assumed compromised at that point) |
| **Impact** | High |
-| **Mitigations** | • npm rejects re-publishing an existing version - re-tagging you cannot overwrite the published `1.15.0`.
• Provenance attestation records the commit SHA the tag pointed to _at publish time_ - forensically verifiable.
• GitHub tag protection rules can prevent tag deletion/force-push. |
+| **Mitigations** | • npm rejects re-publishing an existing version - re-tagging you cannot overwrite the published `1.15.0`.
• Provenance attestation records the commit SHA the tag pointed to _at publish time_ - forensically verifiable. Consumers can confirm with `npm audit signatures axios` (documented in SECURITY.md).
• **Tag protection rules**: repository setting must forbid tag deletion and force-push for the `v1.*.*` pattern. This is a GitHub UI setting (Settings → Tags → rulesets), not file-based — enforcement is auditable via the Rulesets REST API. |
| **Gaps** | A _new_ malicious version (`v1.x.x`) is still publishable by anyone with tag-push rights - this collapses back into T-S3 (account security). |
---
@@ -450,16 +449,62 @@ This is the model that protects **what gets published as `axios` on npm**. A suc
| Threat | Likelihood | Impact | Current Posture | Priority Gap |
| ---------------------------- | ---------- | ------------ | --------------- | --------------------------------------------------------------------- |
-| T-S1 Malicious PR | High | Critical | 🟡 Adequate | Path-scoped CODEOWNERS for `lib/`, `.github/workflows/`, build config |
-| **T-S2 Dev-dep steals keys** | **Medium** | **Critical** | **🔴 Weak** | **Local `--ignore-scripts`; no publish tokens on workstations** |
-| **T-S3 Phishing** | **High** | **Critical** | 🟡 Partial | Mandate WebAuthn (not TOTP) for maintainer GitHub accounts |
+| T-S1 Malicious PR | High | Critical | 🟢 Good | Second maintainer to enable two-person review on scoped paths |
+| **T-S2 Dev-dep steals keys** | **Medium** | **Critical** | 🟡 Partial | **Isolated dev environment (devcontainer/VM); no publish tokens on workstations** — lifecycle scripts now blocked via project `.npmrc`, but build-tool plugins still execute |
+| T-S3 Phishing | High | Critical | 🟢 Good | Document phish-response runbook; require registered backup hardware key |
| T-S4 Runtime dep compromise | Low | Critical | 🟢 Good | - |
-| T-S5 Build tampering | Low | Critical | 🟡 Adequate | Reproducible-build verification step |
-| T-S6 Workflow tampering | Low | Critical | 🟢 Good | Path-scoped CODEOWNERS entry for `.github/workflows/**` |
+| T-S5 Build tampering | Low | Critical | 🟡 Adequate | Eliminate build non-determinism, then promote reproducibility check to blocking |
+| T-S6 Workflow tampering | Low | Critical | 🟢 Good | Second maintainer (two-person review) for `/.github/workflows/` |
| T-S7 Tag replay | Low | High | 🟢 Good | - |
| T-S8 Typosquat | High | Medium | ⚪ Out of scope | - |
-**The two threats most worth investing in are T-S2 and T-S3.** Both target the maintainer rather than the code, both bypass every in-repo control, and both have well-understood, low-cost mitigations that are currently a matter of individual maintainer discipline rather than enforced policy.
+**The top remaining investment is T-S2** (dev-dependency compromise of maintainer workstations). Lifecycle-script execution is now blocked by the project-level `.npmrc`, and T-S3 phishing risk dropped materially once all maintainers moved to hardware-backed WebAuthn — real-time credential relay no longer works. The residual T-S2 gap is build-tool plugin execution (Rollup/Babel/Vitest/ESLint), which `ignore-scripts` does not cover; closing it requires running builds in an isolated environment without access to long-lived credentials.
+
+---
+
+### 3.7 Incident Response Runbook
+
+If a maintainer suspects credential compromise (phish clicked, lost hardware key, unexpected tag/publish, leaked token in logs), execute the steps below **in order**. Speed matters more than completeness — a published malicious version affects every downstream consumer.
+
+**1. Contain — minutes 0–15**
+
+- **GitHub**: revoke all active sessions (`https://github.com/settings/sessions`), revoke all OAuth/PAT tokens (`/settings/tokens`, `/settings/applications`), review authorized SSH keys and remove any unrecognised. If a PAT with `repo` or `admin:org` scope existed, assume leak.
+- **npm**: `npm token list` → `npm token revoke ` for any publish-capable token. If no CLI access, revoke via `https://www.npmjs.com/settings//tokens`. Rotate npm password + force sign-out of all sessions.
+- **Workstation**: if a build/install ran malicious code, **assume full-user compromise** of the laptop. Unplug from trusted networks. Do not rely on AV; move to clean machine for rotation steps.
+
+**2. Assess — minutes 15–60**
+
+- Check `https://github.com/axios/axios/settings/security-log` and `https://github.com//security/log` for unrecognised events (key adds, org changes, force-pushes, new tags).
+- Verify recent tags match intent: `git log --tags --oneline -n 20`. Compare with `https://www.npmjs.com/package/axios?activeTab=versions`.
+- For each recent publish, verify provenance: `npm audit signatures axios@` and cross-check the `sourceCommit` in the provenance attestation against the tag's SHA. Divergence = investigate.
+- Review `~/.npmrc`, `~/.ssh/`, `~/.config/gh/hosts.yml`, `~/.gnupg/` for tampering and unexpected files.
+
+**3. Rotate — hour 1–4**
+
+- Generate new SSH keys on clean hardware. Remove old keys from GitHub. If using `sk-ssh-ed25519@openssh.com`, register new hardware key first, then deregister the old one — do not leave the account with zero registered keys.
+- Re-enrol WebAuthn authenticators (both primary and backup). Deregister lost/compromised authenticators.
+- Rotate GPG keys if signed commits are used; upload new key to GitHub.
+- Rotate any cloud credentials (`~/.aws/`, `~/.config/gcloud/`) and any tokens present on the compromised machine.
+
+**4. Notify — hour 1 onward**
+
+- **npm security**: `security@npmjs.com`. Include package name, suspected versions, timeline.
+- **GitHub security**: `https://github.com/contact` → Security category. Request an investigation of the account.
+- **Downstream**: open a GitHub security advisory (`https://github.com/axios/axios/security/advisories/new`) as soon as a malicious version is confirmed published. Do **not** wait for a fix — users need to pin away from the bad version.
+- Co-maintainers (when present): notify via out-of-band channel (phone/Signal), not via a potentially compromised channel.
+
+**5. Unpublish / deprecate — hour 1–24**
+
+- npm allows `npm unpublish @` within **72 hours** of publish. After that, use `npm deprecate @ ""` with a message pointing to the advisory.
+- Publish a patched version that bumps semver above the malicious one, so `^` consumers move forward automatically.
+
+**6. Post-mortem — within 1 week**
+
+- Write up timeline: initial vector, dwell time, scope, mitigations applied.
+- Update this threat model if the incident reveals a gap not captured here.
+- File a PR if any mitigation can be codified (new CI check, new lint rule, new CODEOWNERS path).
+
+Keep this runbook current. A runbook no one has rehearsed is a document, not a control.
---
diff --git a/docs/es/pages/misc/security.md b/docs/es/pages/misc/security.md
index ec42f6d4..2cf969f7 100644
--- a/docs/es/pages/misc/security.md
+++ b/docs/es/pages/misc/security.md
@@ -19,21 +19,67 @@ axios.defaults.maxBodyLength = 10 * 1024 * 1024;
El valor por defecto no se ha endurecido porque hacerlo romperá silenciosamente cualquier descarga legítima mayor al límite elegido. La responsabilidad de escoger un tope seguro para fuentes no confiables recae en la aplicación.
+## Verificar una publicación
+
+Cada tarball de `axios` publicado en npm proviene de GitHub Actions y lleva una [atestación de provenance de npm](https://docs.npmjs.com/generating-provenance-statements) que vincula criptográficamente el paquete al workflow y al SHA del commit que lo generó.
+
+Los consumidores pueden verificar la provenance localmente:
+
+```bash
+# Verifica todos los paquetes de tu lockfile, incluido axios
+npm audit signatures
+```
+
+Una verificación exitosa demuestra que el tarball fue construido en el entorno de GitHub Actions de `axios/axios` sobre un commit conocido — no fue alterado entre la compilación y el registro. **No** demuestra que el código de ese commit esté libre de bugs.
+
+Si `npm audit signatures` reporta una atestación ausente o inválida para una versión reciente de `axios`, trátalo como un posible incidente de cadena de suministro y repórtalo por el canal privado indicado abajo.
+
## Reportar una vulnerabilidad
Si crees haber encontrado una vulnerabilidad de seguridad en el proyecto, por favor repórtanosla como se describe a continuación. Tomamos todas las vulnerabilidades de seguridad con seriedad. Si has encontrado una vulnerabilidad en una librería de terceros, por favor repórtala a los responsables de esa librería.
## Proceso de reporte
-Por favor, no reportes vulnerabilidades de seguridad a través de los issues públicos de GitHub. Usa el canal oficial de seguridad en GitHub enviando un [aviso de seguridad](https://github.com/axios/axios/security).
+Por favor, no reportes vulnerabilidades de seguridad a través de los issues públicos de GitHub. Usa el canal oficial de seguridad en GitHub enviando un [aviso de seguridad](https://github.com/axios/axios/security/advisories/new).
## Política de divulgación
-Cuando recibamos un reporte de vulnerabilidad de seguridad, asignaremos un responsable principal. Esta persona se encargará del reporte de la vulnerabilidad. El responsable confirmará el problema y determinará las versiones afectadas. Luego evaluará el problema y determinará la gravedad del mismo. El responsable desarrollará una solución para el problema y preparará una versión. El responsable notificará al reportante cuando la solución esté lista para ser anunciada.
+Cuando recibimos un reporte de vulnerabilidad, asignamos un responsable principal. El responsable confirma el problema, determina las versiones afectadas, evalúa la gravedad, desarrolla y publica una corrección, y coordina la divulgación pública con quien reportó.
+
+### Compromiso de resolución y divulgación en 60 días
+
+Nos comprometemos a **resolver y divulgar públicamente cada aviso de seguridad válido dentro de los 60 días naturales posteriores al reporte inicial**, contados desde el momento en que se recibe el reporte a través del [canal de avisos de seguridad de GitHub](https://github.com/axios/axios/security/advisories/new).
+
+El plazo de 60 días es un compromiso con quienes reportan y con los consumidores aguas abajo — es un mínimo exigible, no una meta. Si no podemos entregar una corrección a tiempo, publicamos de todos modos el aviso el día 60 con la mejor guía de mitigación disponible para que los consumidores puedan actuar.
+
+**Hitos dentro de la ventana de 60 días:**
+
+| Día | Hito |
+| ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 0 | Reporte recibido. Aviso privado abierto en GitHub. |
+| ≤ 3 | Acuse de recibo enviado a quien reporta. Decisión de triaje: dentro de alcance / fuera de alcance / duplicado / falta información. |
+| ≤ 10 | Gravedad evaluada (CVSS v4 cuando aplique). Versiones afectadas confirmadas. CVE solicitado vía GitHub si procede un identificador público. |
+| ≤ 45 | Corrección desarrollada, revisada y probada. Candidata de versión preparada en rama privada. Se ofrece vista previa a quien reporta para validación. |
+| ≤ 60 | Versión parcheada publicada en npm. Aviso público + CVE publicados. Se acredita a quien reportó salvo solicitud contraria. CHANGELOG actualizado. |
+
+**Excepciones y prórrogas.**
+
+- Si quien reporta solicita un embargo más corto (por ejemplo, planea presentar los hallazgos en una conferencia), lo acomodamos cuando sea posible.
+- Si la corrección requiere un cambio disruptivo, coordinación con consumidores aguas abajo importantes, o una publicación upstream de `follow-redirects` / `form-data` / `proxy-from-env`, podemos extender más allá de los 60 días. Cualquier prórroga se divulga públicamente el día 60 vía el aviso, con un ETA revisado y el motivo.
+- Si un reporte está **fuera de alcance** (por ejemplo, cae bajo un non-goal explícito documentado en el [modelo de amenazas](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) del proyecto), lo cerramos con una explicación dentro de la ventana de triaje (≤ 3 días). Los reportes fuera de alcance no entran en la cola de 60 días.
+- Las **vulnerabilidades activamente explotadas** se tratan como incidentes: la corrección y el aviso salen tan pronto como se valide un parche, no según el calendario de 60 días.
+
+**Expectativas sobre quien reporta.**
+
+Mientras un reporte esté bajo embargo, pedimos que se abstengan de divulgación pública hasta el primero de: (a) la publicación coordinada del aviso, o (b) el día 60. Si la fecha límite de 60 días pasa sin acción de nuestra parte, quien reportó queda libre de divulgar por su cuenta — consideraremos eso un fallo nuestro, no suyo.
## Actualizaciones de seguridad
-Las actualizaciones de seguridad se publicarán tan pronto como sea posible después de que el parche haya sido desarrollado y probado. Notificaremos a los usuarios de la versión a través del repositorio GitHub del proyecto. También publicaremos las notas de la versión y los avisos de seguridad en la página de versiones de GitHub del proyecto. Además, marcaremos como obsoletas todas las versiones que contengan la vulnerabilidad de seguridad.
+Las actualizaciones de seguridad se publican tan pronto como sea posible después de desarrollar y probar el parche. Notificamos a los usuarios a través del repositorio GitHub del proyecto y publicamos las notas de la versión y los avisos de seguridad en la página de versiones de GitHub. Además, marcamos como obsoletas todas las versiones que contengan la vulnerabilidad.
+
+## Respuesta a incidentes del lado del mantenedor
+
+Para escenarios de compromiso que afecten cuentas de mantenedores, estaciones de trabajo o infraestructura de publicación (phishing, pérdida de llave hardware, tag o publicación inesperados), el proyecto mantiene un runbook interno de respuesta a incidentes en [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook). Cubre revocación de sesiones, rotación de llaves, notificación aguas abajo y procedimientos de unpublish/deprecate.
## Colaboradores y reconocimientos de seguridad
diff --git a/docs/fr/pages/misc/security.md b/docs/fr/pages/misc/security.md
index 4d2c6933..211e9e7c 100644
--- a/docs/fr/pages/misc/security.md
+++ b/docs/fr/pages/misc/security.md
@@ -19,21 +19,67 @@ axios.defaults.maxBodyLength = 10 * 1024 * 1024;
La valeur par défaut n'a pas été durcie car cela casserait silencieusement tout téléchargement légitime dépassant le plafond choisi. Le choix d'un plafond sûr pour des sources non fiables incombe à l'application.
+## Vérifier une version publiée
+
+Chaque tarball `axios` publié sur npm provient de GitHub Actions et comporte une [attestation de provenance npm](https://docs.npmjs.com/generating-provenance-statements) qui lie cryptographiquement le paquet au workflow et au SHA de commit qui l'a produit.
+
+Les consommateurs peuvent vérifier la provenance localement :
+
+```bash
+# Vérifier chaque paquet de votre lockfile, y compris axios
+npm audit signatures
+```
+
+Une vérification réussie prouve que le tarball a été construit dans l'environnement GitHub Actions de `axios/axios` à partir d'un commit connu — il n'a pas été altéré entre la construction et le registre. Elle ne prouve **pas** que le code de ce commit est exempt de bugs.
+
+Si `npm audit signatures` signale une attestation manquante ou invalide pour une version récente d'`axios`, traitez-le comme un incident potentiel de chaîne d'approvisionnement et signalez-le via le canal privé ci-dessous.
+
## Signaler une vulnérabilité
Si vous pensez avoir trouvé une vulnérabilité de sécurité dans le projet, veuillez nous la signaler comme décrit ci-dessous. Nous prenons toutes les vulnérabilités de sécurité au sérieux. Si vous avez trouvé une vulnérabilité dans une bibliothèque tierce, veuillez la signaler aux mainteneurs de cette bibliothèque.
## Processus de signalement
-Veuillez ne pas signaler les vulnérabilités de sécurité via des issues GitHub publiques. Veuillez utiliser le canal de sécurité officiel sur GitHub en créant un [avis de sécurité](https://github.com/axios/axios/security).
+Veuillez ne pas signaler les vulnérabilités de sécurité via des issues GitHub publiques. Veuillez utiliser le canal de sécurité officiel sur GitHub en créant un [avis de sécurité](https://github.com/axios/axios/security/advisories/new).
## Politique de divulgation
-Lorsque nous recevons un rapport de vulnérabilité de sécurité, nous lui assignons un responsable principal. Cette personne est responsable du rapport de vulnérabilité. Le responsable confirmera le problème et déterminera les versions affectées. Il évaluera ensuite le problème et déterminera la gravité du problème. Le responsable développera un correctif pour le problème et préparera une version. Le responsable informera le rapporteur lorsque le correctif sera prêt à être annoncé.
+Lorsque nous recevons un rapport de vulnérabilité, nous lui assignons un responsable principal. Le responsable confirme le problème, détermine les versions affectées, évalue la gravité, développe et publie un correctif, et coordonne la divulgation publique avec le rapporteur.
+
+### Engagement de résolution et de divulgation sous 60 jours
+
+Nous nous engageons à **résoudre et divulguer publiquement chaque avis de sécurité valide dans les 60 jours calendaires suivant le rapport initial**, à compter du moment où un rapport est reçu via le [canal des avis de sécurité GitHub](https://github.com/axios/axios/security/advisories/new).
+
+L'horloge des 60 jours est un engagement envers les rapporteurs et les consommateurs en aval — un filet de sécurité, pas un objectif. Si nous ne pouvons pas livrer un correctif à temps, nous publions tout de même l'avis au jour 60 avec les meilleures recommandations d'atténuation disponibles, afin que les consommateurs puissent agir.
+
+**Jalons dans la fenêtre de 60 jours :**
+
+| Jour | Jalon |
+| ---- | ----------------------------------------------------------------------------------------------------------------------------------------- |
+| 0 | Rapport reçu. Avis privé ouvert sur GitHub. |
+| ≤ 3 | Accusé de réception envoyé au rapporteur. Décision de triage : dans le périmètre / hors du périmètre / doublon / informations manquantes. |
+| ≤ 10 | Gravité évaluée (CVSS v4 le cas échéant). Versions affectées confirmées. CVE demandé via GitHub si un identifiant public est justifié. |
+| ≤ 45 | Correctif développé, revu, testé. Release candidate préparée sur une branche privée. Aperçu proposé au rapporteur pour validation. |
+| ≤ 60 | Version corrigée publiée sur npm. Avis public + CVE publiés. Rapporteur crédité sauf demande contraire. CHANGELOG mis à jour. |
+
+**Exceptions et prolongations.**
+
+- Si un rapporteur demande un embargo plus court (par exemple pour présenter ses résultats à une conférence), nous nous adaptons dans la mesure du possible.
+- Si un correctif nécessite un changement cassant, la coordination avec des consommateurs en aval majeurs, ou une publication en amont de `follow-redirects` / `form-data` / `proxy-from-env`, nous pouvons prolonger au-delà de 60 jours. Toute prolongation est divulguée publiquement au jour 60 via l'avis, avec une ETA révisée et la raison.
+- Si un rapport est **hors du périmètre** (par exemple, il relève d'un non-objectif explicite documenté dans le [modèle de menaces](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) du projet), nous le clôturons avec une explication au rapporteur dans la fenêtre de triage (≤ 3 jours). Les rapports hors périmètre n'entrent pas dans la file des 60 jours.
+- Les **vulnérabilités activement exploitées** sont traitées comme des incidents : le correctif et l'avis sont publiés dès qu'un patch est validé, hors du calendrier des 60 jours.
+
+**Attentes vis-à-vis du rapporteur.**
+
+Pendant qu'un rapport est sous embargo, nous demandons aux rapporteurs de s'abstenir de toute divulgation publique jusqu'à la plus proche de : (a) la publication coordonnée de l'avis, ou (b) le jour 60. Si l'échéance des 60 jours passe sans action de notre part, les rapporteurs sont libres de divulguer indépendamment — nous considérerons cela comme un échec de notre part, pas du leur.
## Mises à jour de sécurité
-Les mises à jour de sécurité seront publiées dès que possible après que le correctif a été développé et testé. Nous informerons les utilisateurs de la version via le dépôt GitHub du projet. Nous publierons également les notes de version et les avis de sécurité sur la page des versions GitHub du projet. Nous déprécierons également toutes les versions contenant la vulnérabilité de sécurité.
+Les mises à jour de sécurité sont publiées dès que possible après que le correctif a été développé et testé. Nous informons les utilisateurs via le dépôt GitHub du projet et publions les notes de version et les avis de sécurité sur la page des versions GitHub. Nous déprécions également toutes les versions contenant la vulnérabilité.
+
+## Réponse à incident côté mainteneur
+
+Pour les scénarios de compromission affectant les comptes mainteneurs, les postes de travail ou l'infrastructure de publication (phishing, clé matérielle volée, tag ou publication inattendus), le projet maintient un runbook interne de réponse à incident dans [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook). Il couvre la révocation des sessions, la rotation des clés, la notification en aval et les procédures de dépublication/dépréciation.
## Partenaires de sécurité et remerciements
diff --git a/docs/pages/misc/security.md b/docs/pages/misc/security.md
index b561740c..839cbacf 100644
--- a/docs/pages/misc/security.md
+++ b/docs/pages/misc/security.md
@@ -19,21 +19,67 @@ axios.defaults.maxBodyLength = 10 * 1024 * 1024;
The default was not tightened because doing so would silently break every legitimate download larger than the chosen cap. The responsibility to pick a safe ceiling for untrusted sources rests with the application.
+## Verifying a Release
+
+Every `axios` tarball on npm is published from GitHub Actions with an [npm provenance attestation](https://docs.npmjs.com/generating-provenance-statements) that cryptographically binds the package to the workflow and commit SHA that produced it.
+
+Consumers can verify provenance locally:
+
+```bash
+# Verify every package in your lockfile, including axios
+npm audit signatures
+```
+
+A successful verification proves the tarball was built in `axios/axios`' GitHub Actions environment on a known commit — it was not tampered with between build and registry. It does **not** prove the code in that commit is free of bugs.
+
+If `npm audit signatures` reports a missing or invalid attestation for a recent `axios` version, treat it as a potential supply-chain incident and report via the private channel below.
+
## Reporting a Vulnerability
If you believe you have found a security vulnerability in the project, please report it to us as described below. We take all security vulnerabilities seriously. If you have found a vulnerability in a third-party library, please report it to the maintainers of that library.
## Reporting Process
-Please do not report security vulnerabilities through public GitHub issues. Please use the official security channel on GitHub by logging a [security advisory](https://github.com/axios/axios/security).
+Please do not report security vulnerabilities through public GitHub issues. Please use the official security channel on GitHub by logging a [security advisory](https://github.com/axios/axios/security/advisories/new).
## Disclosure Policy
-When we receive a security vulnerability report, we will assign it a primary handler. This person is responsible for the vulnerability report. The handler will confirm the problem and determine the affected versions. The handler will then evaluate the problem and determine the severity of the issue. The handler will develop a fix for the problem and prepare a release. The handler will notify the reporter when the fix is ready to be announced.
+When we receive a security vulnerability report, we assign it a primary handler. The handler confirms the problem, determines affected versions, evaluates severity, develops and ships a fix, and coordinates public disclosure with the reporter.
+
+### 60-day resolution and disclosure commitment
+
+We commit to **resolving and publicly disclosing every valid security advisory within 60 calendar days of the initial report**, measured from the moment a report is received via the [GitHub security advisory channel](https://github.com/axios/axios/security/advisories/new).
+
+The 60-day clock is a commitment to reporters and downstream consumers — a backstop, not an aspiration. If we cannot ship a fix in time, we still publish the advisory at day 60 with the best available mitigation guidance so consumers can act.
+
+**Milestones inside the 60-day window:**
+
+| Day | Milestone |
+| ---- | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| 0 | Report received. Private advisory opened on GitHub. |
+| ≤ 3 | Acknowledgement sent to reporter. Triage decision: in scope / out of scope / duplicate / needs-info. |
+| ≤ 10 | Severity assessed (CVSS v4 where applicable). Affected versions confirmed. CVE requested via GitHub if a public identifier is warranted. |
+| ≤ 45 | Fix developed, reviewed, tested. Release candidate prepared on a private branch. Reporter offered a preview for validation. |
+| ≤ 60 | Patched release published to npm. Public advisory + CVE published. Reporter credited unless they request otherwise. CHANGELOG updated. |
+
+**Exceptions and extensions.**
+
+- If a reporter requests a shorter embargo (e.g. they plan to present findings at a conference), we accommodate where possible.
+- If a fix requires a breaking change, coordinating with major downstream consumers, or a `follow-redirects` / `form-data` / `proxy-from-env` upstream release, we may extend beyond 60 days. Any extension is disclosed publicly at day 60 via the advisory, with a revised ETA and the reason.
+- If a report is **out of scope** (e.g. falls under an explicit non-goal documented in the project's [threat model](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md)), we close it with an explanation to the reporter within the triage window (≤ 3 days). Out-of-scope reports do not enter the 60-day queue.
+- **Actively exploited vulnerabilities** are treated as incidents: fix and advisory ship as soon as a patch is validated, not on the 60-day schedule.
+
+**Reporter expectations.**
+
+While a report is under embargo, we ask reporters to refrain from public disclosure until the earlier of: (a) the coordinated advisory publication, or (b) day 60. If the 60-day deadline passes without action from us, reporters are free to disclose independently — we will treat that as a failure on our part, not on theirs.
## Security Updates
-Security updates will be released as soon as possible after the patch has been developed and tested. We will notify users of the release via the project’s GitHub repository. We will also publish the release notes and security advisories on the project’s GitHub releases page. We will also deprecate all versions that contain the security vulnerability.
+Security updates are released as soon as possible after the patch has been developed and tested. We notify users of the release via the project's GitHub repository and publish release notes and security advisories on the GitHub releases page. We also deprecate all versions that contain the vulnerability.
+
+## Maintainer-side incident response
+
+For compromise scenarios affecting maintainer accounts, workstations, or release infrastructure (phishing, stolen hardware key, unexpected tag/publish), the project maintains an internal incident-response runbook in [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook). It covers session revocation, key rotation, downstream notification, and unpublish/deprecate procedures.
## Security Partners and Acknowledgements
diff --git a/docs/zh/pages/misc/security.md b/docs/zh/pages/misc/security.md
index bcdde584..5479c54f 100644
--- a/docs/zh/pages/misc/security.md
+++ b/docs/zh/pages/misc/security.md
@@ -19,22 +19,68 @@ axios.defaults.maxBodyLength = 10 * 1024 * 1024;
默认值未被调低是因为调低会静默地影响所有合法的大文件下载。为不可信来源选择合理的上限,应由应用自行负责。
+## 验证发布版本
+
+自 `axios` 在 npm 上发布的每一个 tarball 均通过 GitHub Actions 发布,并附带 [npm provenance 证明](https://docs.npmjs.com/generating-provenance-statements),以密码学方式将软件包与生成它的工作流及提交 SHA 绑定。
+
+使用者可在本地验证该证明:
+
+```bash
+# 验证你 lockfile 中的所有包(包括 axios)
+npm audit signatures
+```
+
+验证通过仅证明该 tarball 是在 `axios/axios` 的 GitHub Actions 环境中、基于已知 commit 构建的,且在构建至 registry 之间未被篡改。它**不**证明该 commit 中的代码没有 bug。
+
+如果 `npm audit signatures` 针对较新版本的 `axios` 报告缺失或无效的证明,请视为潜在的供应链事件,并通过下方的私密渠道上报。
+
## 报告漏洞
如果你认为在本项目中发现了安全漏洞,请按照以下说明向我们报告。我们对所有安全漏洞报告都认真对待。如果你发现的是第三方库中的漏洞,请向该库的维护者报告。
## 报告流程
-请勿通过公开的 GitHub issue 报告安全漏洞。请使用 GitHub 官方安全渠道,提交 [security advisory(安全公告)](https://github.com/axios/axios/security)。
+请勿通过公开的 GitHub issue 报告安全漏洞。请使用 GitHub 官方安全渠道,提交 [security advisory(安全公告)](https://github.com/axios/axios/security/advisories/new)。
## 披露政策
-收到安全漏洞报告后,我们将指定一名主要负责人,该负责人负责跟进漏洞报告,确认问题并确定受影响的版本,评估问题严重程度,开发修复方案并准备发布,在修复方案就绪后通知报告人。
+收到安全漏洞报告后,我们将指定一名主要负责人。该负责人负责确认问题、确定受影响版本、评估严重程度、开发并发布修复,并与报告人协调公开披露。
+
+### 60 天解决与披露承诺
+
+我们承诺 **在收到初次报告后 60 个自然日内解决并公开披露每一个有效的安全公告**,计时从通过 [GitHub 安全公告渠道](https://github.com/axios/axios/security/advisories/new) 接到报告的那一刻起算。
+
+这 60 天是对报告人和下游使用者的硬性承诺,是底线而非目标。如果我们无法在期限内发布修复,也会在第 60 天公开发布公告,提供当前可行的最佳缓解指引,让使用者能够采取行动。
+
+**60 天窗口内的各里程碑:**
+
+| 天 | 里程碑 |
+| ----- | ---------------------------------------------------------------------------------------------------- |
+| 0 | 收到报告。在 GitHub 上开启私密公告。 |
+| ≤ 3 | 向报告人发送确认收到。分流决定:在范围内 / 不在范围内 / 重复 / 需补充信息。 |
+| ≤ 10 | 评估严重程度(在适用时使用 CVSS v4)。确认受影响版本。如有必要,经由 GitHub 申请 CVE。 |
+| ≤ 45 | 修复已开发、评审、测试完毕。在私有分支上准备候选版本。向报告人提供预览以便验证。 |
+| ≤ 60 | 已修复版本发布至 npm。公开公告 + CVE 发布。除非报告人另有要求,否则给予署名。更新 CHANGELOG。 |
+
+**例外与延期。**
+
+- 如果报告人要求缩短禁运期(例如计划在会议上披露研究成果),我们会尽量配合。
+- 如果修复需要引入破坏性变更、需与主要下游使用者协调、或依赖 `follow-redirects` / `form-data` / `proxy-from-env` 的上游发布,我们可能将期限延长至 60 天以上。任何延期都会在第 60 天通过公告公开披露,并说明修订后的预期时间与原因。
+- 如果报告**不在范围内**(例如属于项目 [威胁模型](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) 中明确列出的 non-goal),我们会在分流窗口内(≤ 3 天)向报告人说明并关闭。不在范围内的报告不会进入 60 天队列。
+- **正在被实际利用的漏洞** 按事件处理:修复与公告在补丁验证通过后立即发布,而非按 60 天时间表。
+
+**对报告人的期望。**
+
+在报告处于禁运期时,我们请报告人在以下较早发生的时间点之前不要公开披露:(a) 协调披露的公告发布,或 (b) 第 60 天。如果我们未在 60 天内行动,报告人即可自行披露——我们会将其视为我们的失败,而非报告人的失败。
## 安全更新
安全更新将在补丁开发和测试完成后尽快发布。我们将通过项目的 GitHub 仓库通知用户,并在项目的 GitHub Releases 页面发布发版说明和安全公告。我们还将弃用所有包含该安全漏洞的版本。
+## 维护者侧事件响应
+
+对于影响维护者账号、工作站或发布基础设施的入侵场景(钓鱼、硬件密钥丢失、异常 tag/发布),项目在 [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook) 中维护了一份内部事件响应手册。内容涵盖会话吊销、密钥轮换、下游通知、以及取消发布/弃用流程。
+
## 安全合作伙伴与致谢
感谢以下安全研究人员与我们合作,共同保障项目的安全: