@@ -8,6 +8,19 @@ For regular contributors, return to :doc:`contributing`.
88
99.. contents ::
1010 :local:
11+ :depth: 1
12+
13+ .. highlight :: none
14+
15+ Running reproducer code
16+ -----------------------
17+
18+ .. warning ::
19+
20+ When evaluating a bug report or vulnerability report, treat reproducer code as
21+ untrusted. If you don't understand what it does or are unfamiliar with a library
22+ it imports **do not run it ** (and ask the reporter to provide a simpler reproducer).
23+ We also recommend that any reproducers you do run are executed in a container.
1124
1225Creating a new release
1326----------------------
@@ -60,3 +73,287 @@ If doing a minor release:
6073#. Update both ``target-branch `` backports for Dependabot to reference the new branch name in ``.github/dependabot.yml ``.
6174#. Delete the older backport label (e.g. backport-3.8): https://github.com/aio-libs/aiohttp/labels
6275#. Add a new backport label (e.g. backport-3.10).
76+
77+ Incident response
78+ -----------------
79+
80+ This section covers responding to a reported security vulnerability and to
81+ three classes of compromise -- of the supply chain, of a maintainer account,
82+ and of the project infrastructure. It picks up *after * a report or a problem
83+ is in hand.
84+
85+ .. note ::
86+
87+ Vulnerability reports arrive through the organization's ``SECURITY.md ``
88+ -- GitHub private vulnerability reporting, or email to the security coordinators.
89+ Never triage a suspected vulnerability in a public issue or pull request.
90+
91+ The security coordinator who first triages a report is its *incident lead *,
92+ and may explicitly hand off to another coordinator. The lead owns the GitHub
93+ Security Advisory (GHSA) draft, fix coordination, the release, and notification.
94+
95+ Severity tiers
96+ ~~~~~~~~~~~~~~
97+
98+ aiohttp is volunteer-maintained, so there is no response-time commitment. The
99+ tier guides prioritization, the release decision, and how widely the fix is
100+ announced. All security fixes -- regardless of tier -- land on ``master `` and
101+ are backported to the currently supported ``x.y `` branch (and the next
102+ ``x.y `` branch, if one is in development).
103+
104+ High
105+ A concrete remote impact an attacker can actually achieve against a
106+ default deployment. Examples: a single request that stops the server from
107+ handling further requests (a server-wide denial of service), reading files
108+ on the server outside the project root, remote code execution,
109+ authentication bypass. Cut a dedicated security release once the fix lands.
110+
111+ Medium
112+ Bounded impact, or impact that needs a non-default option, an unusual
113+ configuration, or a local position. Examples: request smuggling demonstrated
114+ to cause a real issue against a common proxy, or a DoS that use significant server
115+ resources with a low effort sustained attack.
116+ Cut a dedicated security release.
117+
118+ Low
119+ Limited or hard-to-exploit impact, or impact confined to debug or
120+ non-default paths. Examples: minor version or error-message disclosure,
121+ parser leniency with no demonstrated security impact, or request smuggling
122+ that has not been demonstrated to cause a real issue against a common
123+ proxy. May ride the next routine release rather than cutting a dedicated
124+ security release.
125+
126+ Severity is rated by demonstrated attacker impact. A denial of service that
127+ disables further request handling for the whole server from a single request
128+ leans High; one that requires sustained low-effort traffic to consume
129+ significant server resources leans Medium. Request smuggling and parser bugs
130+ are rated by what an attacker can actually achieve against a common proxy or
131+ in a realistic deployment, not by the shape of the bug.
132+
133+ Responding to a reported vulnerability
134+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135+
136+ #. **Acknowledge and confirm. ** Reply to the reporter in the GitHub private
137+ report or the email thread. Reproduce the issue
138+ (`but don't run untrusted code <Running reproducer code _>`_) against
139+ ``master `` and the maintained ``x.y `` branches. If it is not reproducible
140+ or is out of scope, record the decision and close the report.
141+ #. **Open the GHSA draft. ** For an email report, if the reporter can't use Github
142+ for some reason, then open a new advisory to track the issue.
143+ #. **Develop the fix privately ** in the GHSA's temporary private fork ("Start a
144+ temporary private fork" in the draft) -- never on a public branch or in a
145+ public pull request. Include a regression test unless it would provide too
146+ much information for an attacker to replicate.
147+ #. **Title the advisory ** with the vulnerability class and affected component.
148+ #. **Write the description ** using the GHSA description template::
149+
150+ ### Summary
151+
152+ <one or two sentences describing the vulnerability>
153+
154+ ### Impact
155+
156+ <who is affected, prerequisites, and what an attacker achieves>
157+
158+ ### Workaround
159+
160+ <if relevant, any workarounds for users unable to upgrade>
161+
162+ ---
163+
164+ Patch: <link to commit fixing the issue on the stable x.y branch>
165+
166+ An important detail that Github will use to decide the CVE severity is exploit
167+ maturity. Try to clarify the likelihood of a vulnerability being attacked
168+ (e.g. if exploit code is available, or active attacks in-the-wild already exist).
169+ #. **Set the affected and fixed versions **.
170+ #. **Assign a severity tier ** (see `Severity tiers `_).
171+ #. **Credit the reporter ** (if report came by email) in the GHSA Credits field.
172+ #. **Credit the developer ** using "Remediation developer" if different from Reporter.
173+ #. **Request a CVE ** by clicking the button in the advisory.
174+ #. **Get the fix reviewed ** in the private fork by at least one other maintainer.
175+ #. **Consider early notify ** for high severity issues
176+ (see `Notifying about a disclosed vulnerability `_).
177+ #. **Coordinate a release. **
178+
179+ #. **Agree the timing. ** For an embargoed high-severity incident, set the
180+ release date to align with the lift of any private-list embargo.
181+ #. **Merge all the private forks ** and create and merge the backports for
182+ each.
183+ #. **Create the release. ** Follow `Creating a new release `_.
184+ #. **Update patch link ** in GHSA description.
185+ #. **Publish the GHSA ** usually around 1 day after release.
186+ #. **Notify ** according to the severity tier
187+ (see `Notifying about a disclosed vulnerability `_).
188+ #. **Run the post-incident steps ** (see `Post-incident `_).
189+
190+ Notifying about a disclosed vulnerability
191+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
192+
193+ Notification is cumulative -- each tier adds to the ones below it. Mind the
194+ timing: the ``linux-distros `` pre-notification happens under embargo, *before *
195+ the release, while everything else happens at or after the release.
196+
197+ Baseline (all severities)
198+ The published GHSA (which feeds the CVE and the GitHub Advisory Database).
199+
200+ Higher (Medium-High)
201+ For most high severity issues, additionally post to the public ``oss-security ``
202+ mailing list once the advisory is public.
203+
204+ Highest (High-Critical)
205+ For the most serious issues, additionally *before * the public release, send
206+ an embargoed pre-notification to the private distribution-security lists -- primarily
207+ ``linux-distros `` -- and attempt to notify affected downstreams. Use the
208+ `vuln_search.py dependents-enumeration script
209+ <https://github.com/aio-libs/.github/blob/master/tools/vuln_search.py> `_
210+ to search aiohttp's GitHub dependents; it needs a GitHub token and may need to be
211+ run over a few days, so start it early.
212+
213+ Keep the embargo as short as practical. Typically an embargo of 1 or 2 days is expected.
214+
215+ Template for the embargoed ``linux-distros `` pre-notification::
216+
217+ To: linux-distros@vs.openwall.org
218+ Subject: aiohttp: <one-line summary of the vulnerability>
219+
220+ We are coordinating disclosure of a security vulnerability in aiohttp
221+ (https://github.com/aio-libs/aiohttp), the asyncio HTTP client/server
222+ library.
223+
224+ Summary
225+ <2-3 sentences: the flaw and its impact>
226+
227+ Affected versions
228+ <e.g. aiohttp < 3.12.15 (all 3.11.x and 3.12.x)>
229+
230+ CVE
231+ <CVE-XXXX-XXXXX, or "requested via GitHub Security Advisory, ID pending">
232+
233+ Fix
234+ <link to the patch, or attach the diff>
235+
236+ Proposed public disclosure date
237+ <YYYY-MM-DD> -- as short as practical. On that date we will publish the
238+ GitHub Security Advisory, ship a patched PyPI release, and post to
239+ oss-security.
240+
241+ This issue is not yet public; please observe the embargo until that date.
242+
243+ Contact: <incident lead name and security email>
244+
245+ Template for the public ``oss-security `` disclosure::
246+
247+ To: oss-security@lists.openwall.com
248+ Subject: CVE-XXXX-XXXXX: aiohttp <one-line summary>
249+
250+ A security vulnerability has been fixed in aiohttp, the asyncio HTTP
251+ client/server library (https://github.com/aio-libs/aiohttp).
252+
253+ CVE: CVE-XXXX-XXXXX
254+ Advisory: <GHSA URL>
255+
256+ Affected versions: <...>
257+ Fixed versions: <...>
258+
259+ Description
260+ <what the flaw is>
261+
262+ Impact
263+ <what an attacker can achieve, and any prerequisites>
264+
265+ Mitigation
266+ <workaround, or "upgrade to <version>">
267+
268+ Post-incident
269+ ~~~~~~~~~~~~~
270+
271+ #. **Update ** ``THREAT_MODEL.md ``. Have an AI coding agent fetch the new advisories
272+ and update the document. Review the changes.
273+ #. **File follow-up hardening ** as ordinary public issues or pull requests.
274+
275+ Supply-chain or release compromise
276+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
277+
278+ A malicious or tampered release on PyPI, or a compromised publish pipeline.
279+
280+ #. **Pull the bad artifact. ** On PyPI, *yank * the affected release -- this
281+ hides it from new resolves while keeping existing pins working. *Delete * it
282+ only if it is actively malicious and removal is the safer choice. This needs
283+ a PyPI project owner or maintainer account.
284+ #. **Verify the legitimate artifacts with Sigstore. ** Every genuine release is
285+ signed by the release pipeline, with the ``.sigstore `` bundles attached to
286+ the GitHub Release. Verify the ``sdist `` and wheels; a divergence from what is on
287+ PyPI localizes the tampering.
288+ #. **Lock the publish path. ** Publishing uses a PyPI OIDC trusted publisher, so
289+ there is no long-lived token to rotate. Instead, on PyPI temporarily remove
290+ the trusted-publisher binding, and on GitHub restrict or pause the ``pypi ``
291+ deployment environment used by the release job.
292+ #. **Audit the release inputs. ** Review recent changes to the release workflow,
293+ its tag trigger, and the third-party action versions it uses.
294+ #. **Audit the committed Cython sources. ** The generated ``.c `` files ship in
295+ the ``sdist `` and are not checked against their ``.pyx `` sources in CI (see
296+ ``THREAT_MODEL.md `` section 5.19). Regenerate them with ``make cythonize ``
297+ and ``git diff `` against the released revision. Re-verify the
298+ ``vendor/llhttp `` pin and ``package-lock.json ``.
299+ #. **Reissue a clean release ** via `Creating a new release `_ once the cause is
300+ fixed. Never re-publish over a yanked version number.
301+ #. **Notify ** per severity -- a malicious published release is High.
302+
303+ Maintainer account compromise
304+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
305+
306+ A GitHub or PyPI account takeover, or leaked publishing or signing material.
307+
308+ #. **Suspend the account. ** Another GitHub organization owner can suspend it,
309+ revoke its repository access, and start the audit-log review. If the
310+ compromised account is the sole organization owner, or no other owner is
311+ reachable, contact GitHub Support at https://support.github.com/contact
312+ for emergency account lock down and organization recovery.
313+ #. **Kill sessions and credentials ** on the affected account. On GitHub: change
314+ the password, sign out all sessions, revoke every personal access token,
315+ OAuth app, and SSH or GPG key, then re-enroll two-factor authentication.
316+ On PyPI: change the password, re-enroll two-factor authentication,
317+ and revoke API tokens.
318+ #. **Audit PyPI project ownership. ** OIDC publishing means there is usually no
319+ long-lived PyPI token to leak, but a compromised project owner can add a
320+ trusted publisher or upload directly. Review the project collaborators and
321+ trusted-publisher bindings, and remove anything unrecognized.
322+ #. **Audit what the account could have done ** from the GitHub audit log:
323+ pushes, tag creation, branch-protection or settings changes, new secrets or
324+ deploy keys, new collaborators, and workflow edits.
325+ #. **Revert and re-verify. ** Force-revert any unauthorized commits or tags, and
326+ re-verify recent releases against their ``Sigstore `` bundles. If a malicious
327+ release shipped, escalate to `Supply-chain or release compromise `_.
328+ #. **Handle signing material. ** ``Sigstore `` signing is key-less, so there is no
329+ static signing key to rotate. Any separate GPG key used for signed tags must
330+ be treated as compromised, then revoked and rotated.
331+
332+ CI or infrastructure compromise
333+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
334+
335+ A compromise of GitHub Actions, repository settings, or branch protections.
336+
337+ #. **Freeze the release path. ** Pause the ``pypi `` deployment environment and
338+ stop tag-triggered deploys until the compromise is scoped.
339+ #. **Review branch protection and required reviews ** on ``master `` and the
340+ ``x.y `` branches. These are enforced on GitHub and are not visible in the
341+ repository; confirm nothing was relaxed and restore the known-good
342+ configuration.
343+ #. **Audit Actions secrets and environments ** -- the repository and
344+ organization secrets, the ``pypi `` environment's protection rules and
345+ reviewers, and any new deploy keys. Confirm the workflow keeps empty
346+ top-level permissions and that ``id-token: write `` is scoped to the deploy
347+ job only.
348+ #. **Diff recently changed workflows ** with ``git log -- .github/workflows/ ``,
349+ especially the release workflow, the auto-merge workflow (which runs in the
350+ privileged ``pull_request_target `` context), and the CodeQL workflow. Look
351+ for added steps, changed action references, or new triggers.
352+ #. **Check the committed Cython sources ** as in `Supply-chain or release
353+ compromise `_ -- regenerate them and ``git diff ``.
354+ #. **Re-pin and rebuild. ** If a third-party action was compromised, pin the
355+ affected actions by full commit hash, re-run CI from a clean known-good
356+ revision, and re-verify with ``Sigstore `` any release made during the suspected
357+ window.
358+ #. **Escalate ** to `Supply-chain or release compromise `_ if a release shipped
359+ through the compromised pipeline.
0 commit comments