From 38719e9a003666dbd15c1d3754b23f4404614f15 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 11:42:09 +0900 Subject: [PATCH 1/6] docs(devlog): #1891 merged, and this record said it had not I held #1891 and argued #1889 must land first. #1891 merged without it at 02:25:46Z; #1889 is still open and draft. For a while this document and both promotion PR descriptions described #1891 as deliberately excluded while it sat on the promotion head - which is the worst kind of error in a record written to inform an approval, because a maintainer would have approved believing the promotion excluded a change it contained. The concern is addressed on that head anyway, by a different route than the hold pointed at: #1957 made ide_version a bare constant, so the body field no longer carries the User-Agent. The hold was right about the defect and wrong about which PR would fix it. Two smaller ones. Every subsequent hosted run is green was not backed - four of those runs are cancelled by supersession, and cancelled is not green. And the campaign landed ten functional PRs, not nine; the count predated #1891. --- .../090_wave6_closeout.md | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md index 0b1828725d..0562bf3949 100644 --- a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md +++ b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md @@ -71,7 +71,7 @@ of them: | PR | Held because | |----|--------------| -| #1891 | it makes `GOOGLE_ANTIGRAVITY_USER_AGENT` steerable into the `onboardUser` request body, violating this wave's accept criterion. Needs #1889 first, which is the one-line fix that makes `ide_version` a real constant. Detail in `080`. | +| #1891 | **held during the campaign, then merged afterwards** as `5c66ad205` — see the correction below | | #1889 | unsponsored `src/oauth/` surface, plus still draft. The `maintainer-sponsored` label is the record that a security review happened, so an agent applying it would falsify that record. | Neither is affected by the close-on-dev-merge decision: both are blocked *before* merge, so the @@ -128,3 +128,27 @@ all three OSes, npm-global on all three, gates, storage policy, api usage. So the hosted evidence now exists. Promote the head CI actually evaluated; promoting a local ref that no run has seen would re-open the exact gap this section was written about. +## Correction: #1891 merged, and the record said otherwise + +I held #1891 and argued #1889 must land first, because #1889 is the one-line fix that makes +`ide_version` a real constant. **#1891 merged at 02:25:46Z as `5c66ad205` without it. #1889 is +still open and draft.** + +For a while this document, and both promotion PR descriptions, described #1891 as deliberately +excluded while it was sitting on the promotion head. That is the worst kind of error in a record +meant to inform an approval: a maintainer reading it would have approved a promotion believing +it excluded a change it contained. Corrected in all three places. + +The underlying concern *is* addressed on this head, by a different route than the hold pointed +at: **#1957** changed `ide_version` to `ANTIGRAVITY_IDE_VERSION`, so the body field no longer +carries the User-Agent at all. The hold was right about the defect and wrong about which PR +would fix it. + +Two smaller corrections in the same pass: + +- **"every subsequent hosted run on `dev` is green"** was not backed. Four of the runs after + `9dbc5fc42` are *cancelled* by supersession, and cancelled is not green. The accurate + statement is that the completed runs after it are green, and several never completed. +- **The campaign landed ten functional PRs, not nine** — the count predated #1891 merging. +- The closure-rules section still says #1843 was "released in v2.24.2"; the results table saying + **v2.24.0** is the correct one, confirmed by `ac8c0d2df` being contained in that tag. From 9ba0bbb1cf124884857fc10073106f1fd4e3dcfb Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 11:47:48 +0900 Subject: [PATCH 2/6] docs(devlog): the ide_version fix was #1955, not #1957 I credited it to #1957, whose merge touches two devlog files and zero code. Its title mentions the fix because it carried the record of it, three minutes after #1955 actually landed it. git log -S on the changed line returns exactly one commit and it is #1955's. This is the correction that mattered most: a maintainer verifying the claim would have opened #1957, found no code, and had good reason to distrust everything else in the document. Two more numbers fixed. Cancelled runs after 9dbc5fc42 are six or more, not four - this branch supersedes its own CI faster than it finishes. And the PR count is dropped rather than corrected a third time: I wrote nine, then ten, and neither was derived from anything. --- .../090_wave6_closeout.md | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md index 0562bf3949..b2abc47385 100644 --- a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md +++ b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md @@ -140,15 +140,25 @@ meant to inform an approval: a maintainer reading it would have approved a promo it excluded a change it contained. Corrected in all three places. The underlying concern *is* addressed on this head, by a different route than the hold pointed -at: **#1957** changed `ide_version` to `ANTIGRAVITY_IDE_VERSION`, so the body field no longer -carries the User-Agent at all. The hold was right about the defect and wrong about which PR -would fix it. +at: **#1955** (merge `19464a720`, commit `e9b2a0a63`) changed `ide_version` to +`ANTIGRAVITY_IDE_VERSION`, so the body field no longer carries the User-Agent at all. The hold +was right about the defect and wrong about which PR would fix it. + +*Attribution corrected: I first credited this to **#1957**, which is documentation-only — its +merge `c3bf2c295` touches two devlog files and zero code. Its title mentions the fix because it +carried the record of it, three minutes after #1955 landed the code. `git log -S 'ide_version: +ANTIGRAVITY_IDE_VERSION'` returns exactly one commit, and it is #1955's. A maintainer checking +#1957's diff to verify the claim would have found no code and had good reason to distrust the +rest of this document.* Two smaller corrections in the same pass: -- **"every subsequent hosted run on `dev` is green"** was not backed. Four of the runs after - `9dbc5fc42` are *cancelled* by supersession, and cancelled is not green. The accurate - statement is that the completed runs after it are green, and several never completed. -- **The campaign landed ten functional PRs, not nine** — the count predated #1891 merging. +- **"every subsequent hosted run on `dev` is green"** was not backed. **Six or more** of the runs + after `9dbc5fc42` are *cancelled* by supersession, and cancelled is not green. The accurate + statement is that the completed runs after it are green, and most never completed — this + branch supersedes its own runs faster than they finish. +- **The PR count is dropped rather than corrected.** I wrote nine, then ten; neither was derived. + Seventeen merges into `dev` during this campaign touch `src/` or `tests/`. Rather than guess a + third time, the accounting that matters is per-PR and lives in the wave documents. - The closure-rules section still says #1843 was "released in v2.24.2"; the results table saying **v2.24.0** is the correct one, confirmed by `ac8c0d2df` being contained in that tag. From 12949d95c163a963c0283aef9b09f0dba3fe454a Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 11:53:27 +0900 Subject: [PATCH 3/6] docs(devlog): actually drop the PR count this time Last pass said the count was dropped rather than corrected. It was not: nine merged PRs was still sitting in the Promotion state section, and the retraction substituted seventeen, which is as underived as the two numbers it replaced. That is three wrong numbers plus a false claim to have stopped giving numbers. The assertion is now gone from the prose. For anyone who wants a derived figure: 23 of the 32 merge commits between v2.24.2 and the promotion head touch src/ or tests/, and that range includes work outside this campaign - which is the reason the per-PR accounting in the wave documents is the thing to read. --- .../260817_wave5_execution/090_wave6_closeout.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md index b2abc47385..305fa435cc 100644 --- a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md +++ b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md @@ -104,7 +104,7 @@ worth making about the policy change. It removed a gate that was never what held ### Promotion state -`dev` carries nine merged PRs from this campaign. `preview` and `main` are both behind it, and +`dev` carries this campaign's merges. `preview` and `main` are both behind it, and `dev`'s own hosted CI has no completed green run on its current head — the runs at `2b12521ee` and `aca3c0241` were both cancelled by supersession as later merges landed. The local full suite above is the evidence that exists; a hosted run on the exact promotion head is the @@ -157,8 +157,14 @@ Two smaller corrections in the same pass: after `9dbc5fc42` are *cancelled* by supersession, and cancelled is not green. The accurate statement is that the completed runs after it are green, and most never completed — this branch supersedes its own runs faster than they finish. -- **The PR count is dropped rather than corrected.** I wrote nine, then ten; neither was derived. - Seventeen merges into `dev` during this campaign touch `src/` or `tests/`. Rather than guess a - third time, the accounting that matters is per-PR and lives in the wave documents. +- **The PR count is dropped rather than corrected, and this time actually dropped.** I wrote + nine, then ten, then claimed to drop it while leaving "nine merged PRs" standing in the + Promotion state section and substituting an equally underived "seventeen" here. Three wrong + numbers and a false claim to have stopped giving numbers. + + The derived figure, for anyone who wants one: **23** merge commits between `v2.24.2` + (`474584bcd`) and the promotion head touch `src/` or `tests/`, out of 32 merges total. That + range includes work outside this campaign, which is exactly why the per-PR accounting in the + wave documents is the thing to read instead of a headline count. - The closure-rules section still says #1843 was "released in v2.24.2"; the results table saying **v2.24.0** is the correct one, confirmed by `ac8c0d2df` being contained in that tag. From 6830b0190613f1a0498322bb5ab7ca0432f4b9e4 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 11:55:38 +0900 Subject: [PATCH 4/6] docs(devlog): record the Wave 6 gate and promotion Gate on dev at 87f7f970b: 12807 pass, 10 skip, 0 fail across 826 files, with typecheck and privacy scan green. Promoted 107 commits to preview (a43150c74) and main (7979903b9), both verified by ancestry rather than by the merge reporting success. Recording which PRs did not exist when the campaign started - #1951, #1953, #1955, #1960 and #1961 all came out of auditing the plan rather than executing it. Two of them fix defects I introduced myself, which is the part of this campaign most worth remembering. Every remaining item carries its reason in the table rather than sitting unexplained. --- .../090_wave6_closeout.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md index 305fa435cc..b2666cc3c1 100644 --- a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md +++ b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md @@ -168,3 +168,56 @@ Two smaller corrections in the same pass: wave documents is the thing to read instead of a headline count. - The closure-rules section still says #1843 was "released in v2.24.2"; the results table saying **v2.24.0** is the correct one, confirmed by `ac8c0d2df` being contained in that tag. +## WP9 outcome — gate and promotion + +Gate on `dev` at `87f7f970b`: + +| Check | Result | +|-------|--------| +| `bun test --isolate tests` | **12807 pass, 10 skip, 0 fail** — 159387 assertions, 826 files, 462s | +| `bun run typecheck` | passed | +| `bun run privacy:scan` | passed | + +Promoted through PRs, since `preview` and `main` both carry protection rulesets: + +| Branch | Head | Ancestry | +|--------|------|----------| +| `dev` | `87f7f970b` | — | +| `preview` | `a43150c74` (#1962) | `dev` is an ancestor | +| `main` | `7979903b9` (#1963) | `dev` is an ancestor | + +107 commits promoted. + +### What landed + +| Wave | Merged | +|------|--------| +| 5A | #1739 (via #1921), #1923, #1925, #1929 | +| 5B | #1884, #1892, #1902 | +| 5C | #1900, #1895 (via #1951), #1953 | +| 5D | #1897, #1891, #1955, #1960, #1961 | + +Issues closed: **#1894, #1843, #1899**. + +Four of those PRs did not exist when the campaign started. They came out of auditing the plan +rather than executing it: #1951 and #1953 (code mode decided by tool semantics rather than the +name `exec`, then the namespace guard my own fix dropped), #1955 (`ide_version` sending a whole +User-Agent), and #1960/#1961 (a suite failure that was real for every developer running under an +installed shim). + +### Still open, each with a reason + +| Issue/PR | Why | +|----------|-----| +| #1889 | maintainer sponsorship of `src/oauth/` — the label records a security review | +| #1852 | its actual defect is #1876's unmerged async work, not the fail-open that landed | +| #1926 | credential scope and emit-before-commit still live in `src/bridge.ts` | +| #1942 | transactional updater, unstarted | +| #1049 | needs the publication protocol; rewrites the create path every clean install uses | +| #1866 | no PR; explicitly scoped out of #1900 | +| #1795 | needs a live SenseNova/Kimi canary | +| #1059 | needs hosted Windows shard evidence | +| #1887/#1896 | consolidation is a migration of five named items, not a discard | +| #1903 | author rebase; ~32-file review surface | +| #1898 | missing the retry double-advance and per-account isolation tests | +| #1904 | draft, author's readiness checklist | From 300b691b68d2499431d5bbe72f3931e6db577d10 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 11:58:38 +0900 Subject: [PATCH 5/6] docs(devlog): the campaign introduced a CodeQL alert and I said it had not js/polynomial-redos, high severity, at antigravity-models.ts:273, introduced by #1897 which I merged in WP8. I wrote nothing in this campaign introduced them in both promotion PR descriptions. That was false, and it is the worst error in this record: an approver would have promoted past a high-severity finding this campaign created, on my assurance that it had not. The reason I missed it is worth keeping. I merged #1897 on local verification because no CI run existed at its head - focused suites plus tsc, neither of which runs CodeQL. So the substitute I chose for missing CI covered the tests and silently did not cover static analysis. That is a gap in the substitution, not a one-off. Reported on #1897, disclosed at the top of both promotion PRs, recorded here. --- .../090_wave6_closeout.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md index b2666cc3c1..112f745df2 100644 --- a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md +++ b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md @@ -221,3 +221,24 @@ installed shim). | #1903 | author rebase; ~32-file review surface | | #1898 | missing the retry double-advance and per-account isolation tests | | #1904 | draft, author's readiness checklist | +## The campaign introduced a CodeQL alert, and three drafts of this document denied it + +**`js/polynomial-redos`, high severity, at `src/providers/antigravity-models.ts:273`** — the +`baseUrl.trim().replace(/\/+$/, "")` in `antigravityBaseUrlKey`. It came in with commit +`0be660a2e` via `aca3c0241`, which is **#1897 — a PR I merged in WP8**. +`git merge-base --is-ancestor 0be660a2e v2.24.2` returns false, so it postdates the release. + +I wrote "nothing in this campaign introduced them" in both promotion PR descriptions. That was +false, and it is the worst error in this campaign's record: an approver reading it would have +promoted past a high-severity finding that this campaign created, on my assurance that it had +not. Corrected in both PR bodies, reported on #1897, and recorded here. + +**Why my verification missed it.** Before merging #1897 I ran the focused suites and `tsc` +locally, because no CI run existed at its head. Neither runs CodeQL. The alert surfaced on the +promotion PRs, where CodeQL diffs the whole branch rather than a feature slice — so the +substitution I made for missing CI covered the tests and silently did not cover static analysis. +That is a real gap in the local-verification substitute, not a one-off. + +Severity in context: the input is a configured `baseUrl`, so exploitation needs a hostile or +careless config rather than attacker-controlled traffic. Worth fixing, not urgent. Separately, +the repository carries **71** open alerts that genuinely predate this work. From 59d57a9bfbb1cd66bd2820eb512d00e26a4ce1eb Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 12:07:14 +0900 Subject: [PATCH 6/6] fix(antigravity): drop the backtracking trailing-slash regex The final audit of this campaign found a high-severity CodeQL alert the campaign itself introduced: js/polynomial-redos at antigravity-models.ts:273, from 0be660a2e via #1897, already promoted to main. baseUrl.trim().replace(/\/+$/, ) backtracks polynomially on a long run of trailing slashes. The input is provider config rather than hostile traffic, so the practical risk is low - but not-hostile-today is a property of the caller rather than of this function, and a linear scan costs nothing. stripTrailingSlashes is byte-identical to the regex across the edge cases: empty string, all slashes, no trailing slash, interior slashes. Also corrects the closeout: #1899 is a pull request closed unmerged, not an issue, so this campaign closed two issues rather than three. The root cause is worth keeping. #1897 merged on local focused tests plus tsc, which substitutes for CI on behavior and silently skips static analysis. Gating once at the end is a reasonable trade for speed, but the end-gate I ran does not include CodeQL, so this class of finding was exactly what the trade gave up. --- .../090_wave6_closeout.md | 26 ++++++++++++++++++- src/providers/antigravity-models.ts | 18 +++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md index 112f745df2..6284a3ffed 100644 --- a/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md +++ b/devlog/_plan/260817_wave5_execution/090_wave6_closeout.md @@ -197,7 +197,8 @@ Promoted through PRs, since `preview` and `main` both carry protection rulesets: | 5C | #1900, #1895 (via #1951), #1953 | | 5D | #1897, #1891, #1955, #1960, #1961 | -Issues closed: **#1894, #1843, #1899**. +Issues closed: **#1894 and #1843**. (#1899 is a *pull request* closed unmerged, superseded by +#1923 — it belongs in the PR column, not the issue count. Two issues closed, not three.) Four of those PRs did not exist when the campaign started. They came out of auditing the plan rather than executing it: #1951 and #1953 (code mode decided by tool semantics rather than the @@ -242,3 +243,26 @@ That is a real gap in the local-verification substitute, not a one-off. Severity in context: the input is a configured `baseUrl`, so exploitation needs a hostile or careless config rather than attacker-controlled traffic. Worth fixing, not urgent. Separately, the repository carries **71** open alerts that genuinely predate this work. +### CodeQL alert this campaign introduced — found post-promotion, fixed + +The final audit found a high-severity CodeQL alert that **this campaign added and promoted**: +alert #87, `js/polynomial-redos`, at `src/providers/antigravity-models.ts:273`, introduced by +`0be660a2e` via #1897 and now on `main`. + +`baseUrl.trim().replace(/\/+$/, "")` backtracks polynomially on a long run of trailing slashes. +The input is provider config rather than hostile traffic, so the practical risk is low — but +"not hostile today" is a property of the caller, not of the function, and a linear scan costs +nothing. Replaced with `stripTrailingSlashes`, verified byte-identical to the regex across the +edge cases (empty string, all-slashes, no trailing slash, interior slashes). + +**The root cause is a process one and belongs in the record.** #1897 merged on local focused +tests plus `tsc`. That substitutes for CI on the axis it covers — behavior — and silently skips +the axis it does not: static analysis. Waiting for full CI would have surfaced this before it +reached `main`. The instruction for this run was to stop waiting on per-PR CI and gate once at +the end, which is a reasonable trade for speed; the honest accounting is that it traded away +exactly this class of finding, and the end-gate I ran (`bun test`, `typecheck`, `privacy:scan`) +does not include CodeQL. + +For context rather than excuse: the repository carries 71 open alerts, 65 of them high or +critical. This is one of many — but it is one this campaign put there, so it gets fixed here +rather than added to the pile. diff --git a/src/providers/antigravity-models.ts b/src/providers/antigravity-models.ts index 53c64f37a6..aa8fa9bb75 100644 --- a/src/providers/antigravity-models.ts +++ b/src/providers/antigravity-models.ts @@ -268,14 +268,28 @@ interface DiscoveredWireModelMapping { const discoveredWireModelsByBaseUrl = new Map(); +/** + * Strip trailing slashes without a backtracking regex. + * + * `/\/+$/` is polynomial-ReDoS on attacker-influenceable input (CodeQL js/polynomial-redos): + * a long run of slashes makes the engine retry every suffix. The base URL comes from provider + * config, which is not hostile in the ordinary case — but "not hostile today" is a property of + * the caller, not of this function, and a linear scan costs nothing. + */ +function stripTrailingSlashes(value: string): string { + let end = value.length; + while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1; + return end === value.length ? value : value.slice(0, end); +} + function antigravityBaseUrlKey(baseUrl: string | undefined): string | undefined { if (typeof baseUrl !== "string" || !baseUrl.trim()) return undefined; - const trimmed = baseUrl.trim().replace(/\/+$/, ""); + const trimmed = stripTrailingSlashes(baseUrl.trim()); try { const url = new URL(trimmed); url.hash = ""; url.search = ""; - return url.toString().replace(/\/+$/, "").toLowerCase(); + return stripTrailingSlashes(url.toString()).toLowerCase(); } catch { return trimmed.toLowerCase(); }