{"owner":"kgateway-dev","repo":"kgateway","hasSkills":true,"totalSkillsCount":6,"totalTokensCount":8152,"categories":["anthropic-skill","copilot-instructions","root-instruction","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".claude/skills/cve-bump/SKILL.md",".github/copilot-instructions.md","AGENTS.md","CLAUDE.md","pkg/kgateway/extensions2/plugins/waypoint/README.md","tools/build-tools/README.md"],"skills":{".claude/skills/cve-bump/SKILL.md":"---\nname: cve-bump\ndescription: Patch CVEs surfaced by the kgateway OSV scanner by bumping the affected Go module dependencies (direct, indirect via require, or replace) and curating false-positive entries in osv-scanner.toml. Use this skill whenever the user wants to fix CVEs, address OSV-Scanner alerts, bump deps for security reasons, triage code-scanning findings, \"see what CVEs we have\", or do anything CVE/security-bump-flavored in this repo — even if they don't name the skill explicitly.\n---\n\n# cve-bump\n\nEnd-to-end workflow for clearing CVEs in kgateway's Go dependencies. The user\nwants a single pass: discover the alerts → decide the action for each →\npresent a plan → apply the bumps and ignore-list updates → verify locally.\n\n## Why this skill exists\n\nCVE patching in kgateway is more mechanical than it looks, but the moving\nparts are easy to miss:\n\n- The scanner findings live in GitHub code-scanning, not the local tree.\n  `hack/osvtool` is the canonical way to fetch them.\n- Findings are uploaded per-branch and only the branches allowlisted in\n  the OSV scan workflow (`.github/workflows/osv-scanner.yaml`) get\n  scanned, so a topic branch has to query its base branch.\n- Some \"vulnerabilities\" are scanner false positives caused by\n  pseudo-version comparison quirks (see the istio.io entries in\n  `osv-scanner.toml`). These belong in the ignore list, not in go.mod\n  churn.\n- Indirect deps need either a `go get` of the parent that pulls in the\n  fixed version, or an explicit `require` to force MVS to pick the fixed\n  version. Getting this wrong looks like the bump \"didn't take.\"\n\nThe skill encodes those decisions so the human only has to approve the\nplan, not relearn the rules each time.\n\n## Protected dependencies\n\nSome dependencies are load-bearing enough that bumping them risks\nbreaking the dataplane, compatibility with managed Kubernetes\nversions, or the Gateway API conformance contract. **Never bump any\nof these without an explicit, in-conversation yes from the user —\neven for a critical CVE.**\n\n- `github.com/envoyproxy/*` (Envoy and `go-control-plane`)\n- `k8s.io/*` (api, apimachinery, client-go, ...)\n- `istio.io/*`\n- `sigs.k8s.io/gateway-api` and other Gateway API modules\n\nHow this gets enforced through the workflow:\n\n- In the plan (phase 4), surface protected-dep rows with the action\n  `needs approval (protected dep)` and list them in their own\n  bucket. Do not fold them into a batch `go get`.\n- In the execute step (phase 5), confirm each protected-dep bump\n  one-by-one before running `go get`. If the user says no, leave\n  the dep alone and list the CVE under \"deferred — protected dep\"\n  in the report.\n- Never silence a protected-dep CVE via `osv-scanner.toml` to avoid\n  asking. The ignore list is for false positives only.\n\nIf a transitive bump (parent dep) would pull a new version of a\nprotected dep through MVS, that still counts as bumping the\nprotected dep. Detect it (`go list -m -json <module>` after the\nparent bump) and bring it back to the user.\n\n## CVEs with no known fix\n\nOSV-Scanner sometimes surfaces advisories that have no fixed\nversion yet (or were withdrawn upstream). Do not:\n\n- Pin to a fork or a personal patch.\n- Add an `osv-scanner.toml` ignore entry just to clear the alert.\n- Invent a \"mitigation\" not stated in the advisory.\n\nInstead, collect these into a \"no fix available\" list during phase\n4 and surface them to the user verbatim in the report (phase 7):\nCVE / severity / module / pinned version / advisory URL. They are\ntracked, not patched.\n\n## Trigger fit\n\nStrong fit when the user says any of:\n\n- \"patch the CVEs\", \"fix the CVEs\", \"clear the OSV alerts\"\n- \"bump X for the CVE\", \"address GHSA-...\", \"what CVEs do we have?\"\n- \"scanner is complaining about Y\", \"osv-scanner findings\"\n- \"update deps for security\"\n\nSkip when the user wants a non-security dep bump (no CVE motivation) —\n`hack/bump_deps.sh` and a plain `go get` are usually right for those.\n\n## Tooling\n\nThis is a Go shop. The repo does not ship a Python toolchain and many\ncontributors do not have one installed — `pip install pyyaml` is **not**\na viable step.\n\nFor every parsing, filtering, or shaping task in this workflow, use:\n\n- **`jq`** for JSON. Required by `hack/osvtool` already, so present\n  whenever osvtool runs.\n- **`yq`** for YAML. Also required by `hack/osvtool`. Use\n  `yq -p=yaml -o=json` to convert the osvtool YAML back into JSON so\n  `jq` can do the heavy lifting.\n- **`go`**, `make`, standard shell builtins.\n\nDo **not** reach for `python`, `python3`, `node`, `ruby`, or\n`pip`/`npm install <anything>`. If you find yourself wanting one, write\na `jq` expression instead — every shape in the osvtool output (alerts,\nrule metadata, message text, help tables) is straightforward to handle\nin jq.\n\nConcrete starter: to round-trip the saved YAML back to a JSON array of\nalerts and start filtering:\n\n```bash\nyq -p=yaml -o=json /tmp/cve-bump/alerts-<branch>-<ts>.yaml \\\n  | jq '[ .[] | {\n      ghsa: .rule.id,\n      sev:  .rule.security_severity_level,\n      msg:  .most_recent_instance.message.text,\n      help: .rule.help,\n      url:  .html_url\n    } ]'\n```\n\nCVE IDs typically appear in `rule.full_description` or in the message\ntext alongside the package and version; the `help` field contains the\nGO-XXXX-XXXX advisory table with fixed versions. Extract with `jq`'s\n`capture` / regex helpers or a small `sed`/`grep` pipeline, not Python.\n\n## Workflow\n\nThere are six phases. Do them in order; do not skip the planning phase\neven if there is only one finding.\n\n### 1. Show the landscape (cross-branch summary)\n\nBefore pulling raw alerts, run the table mode across all supported\nbranches **for both targets** so the user sees source and image\nfindings side by side. With no `--branch` flags, `osvtool` scans\nexactly the branches allowlisted in the OSV scan workflow\n(`.github/workflows/osv-scanner.yaml`), so this stays correct as LTS\nbranches are cut or retired:\n\n```bash\n# Source-target landscape (the planning input for this skill)\n./hack/osvtool --table --target source --state open\n\n# Image-target landscape (will be revisited in phase 8)\n./hack/osvtool --table --target image --state open\n```\n\nEach call emits a compact markdown table — one row per branch,\ncolumns: `branch | C | H | M | L | max age | last scan |\ncode-scanning`. Show both to the user as-is; together they fit in\none screen and make image vs. source scope explicit.\n\nUse `--state open` (not `todo`) here so the medium/low counts are\nvisible. The user is about to decide whether to include them; they\nshould see the numbers first.\n\n**Do not** use the default YAML summary mode (without `--table`) for\nthis — it emits ~60 lines per branch of nested fields the user does\nnot need. If you ever want the full structured form (e.g. to grep a\nspecific field), pipe it to a file under `/tmp/cve-bump/` instead of\ninto the chat.\n\n### 2. Decide severity scope and target branch\n\nAsk the user two short questions, back to back:\n\n1. **Severity scope**: just critical/high (`--state todo`), or all open\n   severities including medium/low (`--state open`)? Recommend\n   critical/high since that matches how teams typically prioritize, but\n   the landscape view from phase 1 shows the full severity breakdown so\n   the user can make an informed call (e.g. if there are 30 mediums on\n   `v2.2.x` that they want to clear ahead of a release). Just ask in\n   one sentence — no menu.\n\n2. **Branch to plan against**: the raw pull is single-branch because\n   planning depends on that branch's go.mod state. Run the detection\n   helper for a default:\n\n   ```bash\n   .claude/skills/cve-bump/scripts/detect_base.sh\n   ```\n\n   It prints one line — the supported branch the current HEAD is most\n   likely based on (most-recent merge-base across the branches the OSV\n   scan workflow allowlists, read from\n   `.github/workflows/osv-scanner.yaml`), or `main` if it cannot\n   decide. Confirm with the user: \"Planning against `<X>`. OK, or pick\n   another?\"\n\nIf the landscape view in phase 1 showed zero alerts at the chosen\nseverity for the chosen branch, stop here and tell the user — there is\nnothing to plan.\n\n### 3. Pull the raw alerts\n\n```bash\n./hack/osvtool --raw --branch <branch> --target source --state <state>\n```\n\nWhere `<state>` is whatever the user picked in phase 2 (`todo` or\n`open`).\n\nNotes:\n\n- `--target source` filters this pass to Go-mod findings only.\n  Image findings are addressed separately in phase 8 because their\n  actions are different (Dockerfile / base-image / apk changes) and\n  many of them resolve transitively once the source bumps land.\n  Do not try to plan them in the same table.\n- The output is YAML. Save it to a scratch path outside the repo so it\n  is not accidentally committed. Use\n  `/tmp/cve-bump/alerts-<branch>-<UTC-timestamp>.yaml` (mkdir -p first).\n  This gives the user a record and lets us re-parse without another\n  GitHub call.\n\nIf `osvtool` errors with an auth message, surface the help text it\nprints — it already explains the fix (`gh auth refresh --scopes\nsecurity_events` or a fine-grained PAT). Do not paper over it.\n\n### 4. Plan one action per finding\n\nFor each alert, extract:\n\n- CVE ID (`rule.id` is typically the GHSA; the CVE lives in\n  `rule.full_description` or `most_recent_instance.message.text`).\n- Severity (`rule.security_severity_level`).\n- Affected Go module + current pinned version.\n- Fixed version (the lowest version that resolves the advisory).\n  May be **absent** — see the no-fix branch below.\n- Whether the module is direct or indirect in this repo's go.mod.\n- Whether the module is a [protected dependency](#protected-dependencies).\n\nThen pick an action per the decision tree below.\n\n#### Decision tree\n\n```mermaid\nflowchart TD\n    A[Finding] --> N{Is there a fixed version<br/>in the advisory?}\n    N -- no --> NF[Report only:<br/>add to \"no fix available\" list]\n    N -- yes --> P{Is the module a protected dep<br/>envoy / k8s.io/* / istio.io/* / gateway-api?}\n    P -- yes --> PA[Needs approval:<br/>row in plan with action \"needs approval protected dep\"<br/>do not include in batch go get]\n    P -- no --> B{Is the pinned version actually >= fix,<br/>once you account for pseudo-versions?}\n    B -- yes --> C[False positive → add to osv-scanner.toml]\n    B -- no --> D{Is the module direct in go.mod?}\n    D -- yes --> E[go get module@fixed-version]\n    D -- no --> F{Does a single parent dep<br/>own this transitive?}\n    F -- yes, and bumping it is reasonable --> G[go get parent@version-that-pulls-fix]\n    F -- no, or parent bump too risky --> H[Add explicit require<br/>go get module@fixed-version<br/>force MVS to pick the fix]\n```\n\n**Detecting pseudo-version false positives**: when the affected version\nlooks like `v0.0.0-<14-digit-timestamp>-<sha>` and the fix version is a\nnormal semver like `1.x`, OSV-Scanner compares them lexically as\n`\"0.0.0\" < \"1.x\"` and fires regardless of whether the pinned commit\nalready includes the fix. Read the timestamp embedded in the\npseudo-version and compare to the fix release date. If the pinned\ncommit is newer, it is a false positive — add to `osv-scanner.toml`\nwith a reason that names the timestamp, the fix release date, and the\nyears/months gap. The five istio.io entries already in that file are\nthe model to copy.\n\n**Choosing between parent bump and explicit require**: prefer the\nparent bump when (a) the parent is itself behind by only a minor/patch\nversion, and (b) its changelog does not flag breaking changes for our\nusage. Otherwise add an explicit require — it is the smallest possible\nintervention and Go's MVS will honor it.\n\n#### Output: the plan\n\nPresent the plan as up to three fenced tables so each bucket is\nvisually distinct.\n\n**1. Actionable bumps** — what you intend to `go get` or ignore.\nColumns: `CVE | severity | module | from → to | action`.\n\n```\n| CVE             | sev | module                | from           | to        | action                |\n| --------------- | --- | --------------------- | -------------- | --------- | --------------------- |\n| CVE-2025-1111   | H   | golang.org/x/net      | v0.20.0        | v0.27.0   | go get (direct)       |\n| CVE-2025-2222   | M   | gopkg.in/foo.v3       | v3.1.0         | v3.2.1    | explicit require      |\n| CVE-2022-31045  | H   | istio.io/istio        | v0.0.0-2026... | (n/a)     | ignore: false pos     |\n```\n\n**2. Needs approval — protected deps** (omit the table if empty).\nColumns: `CVE | severity | module | from → to | reason`. The user\nmust say yes per row before any of these move into the actionable\nbatch.\n\n```\n| CVE             | sev | module                  | from     | to       | reason                  |\n| --------------- | --- | ----------------------- | -------- | -------- | ----------------------- |\n| CVE-2025-9999   | H   | k8s.io/apimachinery     | v0.31.2  | v0.31.5  | only fix is a k8s.io bump |\n```\n\n**3. No fix available** (omit the table if empty). Columns:\n`CVE | severity | module | pinned | advisory`. Read-only — no\naction proposed. These will be repeated verbatim in the final\nreport.\n\n```\n| CVE             | sev | module                | pinned    | advisory                                        |\n| --------------- | --- | --------------------- | --------- | ----------------------------------------------- |\n| CVE-2025-7777   | M   | example.com/foo       | v1.4.0    | https://github.com/advisories/GHSA-xxxx-yyyy-zz |\n```\n\nThen ask the user to OK the actionable table, decide each\nprotected-dep row, and acknowledge the no-fix list. Wait for\nexplicit approval before touching files.\n\n### 5. Execute\n\nAfter approval, in order:\n\n1. **Protected-dep gate.** For any row whose action was `needs\n   approval (protected dep)`, only proceed if the user explicitly\n   said yes to that row. If unclear, ask again. Do not batch\n   protected deps into step 2 — `go get` them one at a time so the\n   user can stop the flow if a transitive surprise shows up.\n2. For each remaining `go get` action: `go get <module>@<version>`.\n3. `go mod tidy` once at the end.\n4. **Check for surprise protected-dep transitives.** After\n   `go mod tidy`, diff `go.mod` for any envoy / k8s.io / istio.io /\n   gateway-api lines that changed and were not in the approved\n   plan. If any did, stop, surface them to the user, and let them\n   decide (revert or approve).\n5. For each ignore action: append a `[[IgnoredVulns]]` entry to\n   `osv-scanner.toml`. The `reason` must be specific — name the\n   pseudo-version timestamp, the fix release date, and the gap. Do not\n   write generic reasons like \"false positive\" or \"not applicable\".\n   Never add an ignore entry for a no-fix CVE or a protected-dep\n   CVE just to clear noise.\n6. If `go.sum` changed, run `make generate-licenses` to refresh the\n   license attribution files.\n7. Run `make fmt-changed` to keep the diff clean.\n\nDo not stage anything (`git add`) and do not commit. The user reviews\nthe dirty tree themselves.\n\n### 6. Verify\n\nThe user signed off on \"build + scanner re-run only\", so:\n\n1. `go build -tags e2e ./...` — the `e2e` tag is required to compile\n   all source per the repo's CLAUDE.md. If the build fails, the bump is\n   not safe; stop and report.\n2. Local scanner re-run: **`make osv-scan`**. This is the canonical\n   way to run OSV-Scanner locally in kgateway — it shells out to\n   Docker (image `ghcr.io/google/osv-scanner-action`), so you do not\n   need a local `osv-scanner` binary. Do **not** reach for a\n   standalone `osv-scanner` invocation; the make target handles config\n   lookup, output paths, and reporter conversion in one shot.\n   - Results land at `_output/osv/<safe-branch>/results.json` (and a\n     SARIF sibling). `<safe-branch>` is the current branch with `/` and\n     `.` replaced by `-`.\n   - The `make osv-scan` JSON schema is osv-scanner native\n     (`results[].packages[].vulnerabilities[].id`), which is different\n     from the GitHub code-scanning YAML from phase 3. To diff, extract\n     the set of vuln IDs from each side with `jq`/`yq` and compare the\n     sets — don't try to align records field-by-field. Report which\n     CVEs the user expected to clear that still appear, and any new\n     ones the scanner surfaced that were not in the original plan.\n3. If Docker is not running, `make osv-scan` will fail with a Docker\n   error. Tell the user and stop — do not try to install osv-scanner\n   from source as a workaround.\n\n### 7. Report\n\nEnd with the following sections, in order:\n\n1. **What changed.** One line per CVE that got an action: CVE,\n   module, from → to (or \"ignore: <reason summary>\"), and the\n   file(s) touched. The user reads this against `git diff`.\n2. **Deferred — protected dep.** Any protected-dep rows the user\n   said no to (or did not respond to). List CVE / module / proposed\n   version / why deferred so the next maintainer has the context.\n3. **No fix available.** Verbatim from the phase-4 table — CVE /\n   severity / module / pinned / advisory URL. Do not propose\n   workarounds. Do not add `osv-scanner.toml` entries for these.\n\nThen continue into phase 8.\n\n### 8. Image-target follow-up\n\nImage findings often resolve transitively once source bumps land\n(e.g. a Go binary baked into the image gets the fixed dep), but\nsome do not — base-image CVEs and apk packages need Dockerfile or\nbase-image changes.\n\nRefresh the image landscape against the planned branch:\n\n```bash\n./hack/osvtool --table \\\n    --branch <branch> \\\n    --target image --state open\n```\n\nThen prompt the user: \"The source bumps are done. Image findings\nabove remain on the published scan — most should clear once these\nbumps ship, but base-image / apk CVEs need a Dockerfile change.\nWant to plan that now, or defer?\"\n\nTwo important caveats to state plainly:\n\n- The `--table` view reflects the **last GitHub-uploaded scan**,\n  not the working tree. The source bumps you just made won't be\n  reflected in image scans until CI rebuilds and republishes\n  results.\n- A local image scan is possible via\n  `make osv-scan OSV_SCAN_IMAGES=\"<image-ref> ...\"` (see\n  `osv-scan-latest-main-images` in the Makefile for the canonical\n  invocation against rolling-main images), but that scans\n  **already-built published images**, not the just-bumped working\n  tree. Mention this if the user asks why a \"fixed\" CVE still\n  appears locally.\n\nIf the user says \"defer\", stop here. If they say \"go\", treat the\nimage flow as a separate planning round — start a new plan with\nDockerfile-level actions (do not extend the Go-mod plan with image\nactions, they live in different files and the decision tree above\ndoes not apply).\n\n## Edge cases worth knowing\n\n- **Pseudo-version on the fix side**: rare, but if a fix only exists at\n  a non-tagged commit, `go get module@<sha>` works. Mark this clearly\n  in the plan so the user is not surprised by a pseudo-version in\n  go.mod.\n- **Module renamed/moved**: if the advisory's module path no longer\n  exists upstream, a `replace` directive may be necessary. Treat this\n  as ambiguous and flag it for the user instead of writing the replace\n  silently.\n- **Multiple CVEs on one module**: combine into a single `go get` to\n  the highest required version. List them as separate rows in the plan\n  table but mark \"(combined bump)\" in the action column.\n- **Backport branches**: when scanning `v2.2.x` etc., the supported\n  upgrade window for a given module may be narrower (e.g. cannot move\n  to a new major). Default to the smallest version that resolves the\n  CVE within the same major.\n- **Topic branch with no alerts on the base branch**: possible if the\n  branch was just rebased after a recent scan. Tell the user; offer to\n  re-run after CI uploads fresh results.\n\n## Files this skill touches\n\n- `go.mod` / `go.sum` — version bumps\n- `osv-scanner.toml` — false-positive ignore entries (root-level; this\n  is the only scanner ignore file in this repo as of writing — `.snyk`,\n  `.grype.yaml`, `.trivyignore` are not present)\n- License attribution files, if `make generate-licenses` is wired up\n\n## Bundled scripts\n\n- `scripts/detect_base.sh` — prints the supported branch that the\n  current HEAD is most likely based on. Used in phase 1.\n\n## Things not to do\n\n- Do **not** commit or stage. The user does that.\n- Do **not** edit go.mod by hand — always go through `go get` so\n  go.sum stays consistent.\n- Do **not** bump a [protected dependency](#protected-dependencies)\n  without an explicit, in-conversation yes from the user — not in a\n  batch, not as a transitive pulled in by a parent bump. Surface it\n  for approval and wait.\n- Do **not** invent a workaround for a CVE with no upstream fix.\n  Do not pin to a fork, do not silence it via `osv-scanner.toml`.\n  List it in the no-fix bucket and move on.\n- Do **not** stop after the source bumps — phase 8 (image\n  follow-up) is part of the workflow. Prompt the user about\n  remaining image findings every time.\n- Do **not** add an `osv-scanner.toml` entry just to silence noise.\n  The reason field has to defend the entry to a future reader.\n- Do **not** run `make generate-all` or other heavy codegen targets\n  for a dep bump — they are not needed for non-API changes and they\n  slow the loop down by ~30s for no benefit.\n- Do **not** scan an unsupported branch (e.g. `chandler/cveskill`)\n  directly via `osvtool` — there are no GitHub-uploaded alerts for it.\n  (Note: `make osv-scan` does work on any branch because it runs the\n  scanner locally; that is fine for verification.)\n- Do **not** reach for Python, Node, or any other parser. Use `jq`\n  and `yq` — see the Tooling section.\n- Do **not** invoke `osv-scanner` directly. Use `make osv-scan` — it\n  runs the scanner in Docker with the right config and output paths.\n- Do **not** dump the default YAML summary into the chat for the\n  cross-branch landscape view. Use `./hack/osvtool --table` (phase 1)\n  — it's a single small markdown table instead of dozens of lines of\n  nested YAML.\n",".github/copilot-instructions.md":"# Go Version\nThis project uses **Go 1.26**.\n\n# Syntax Overrides\nThe following syntax is valid in Go 1.26 and must NOT be flagged as an error:\n- `new(value)`: The `new()` function now accepts expressions/values, not just types.\n  - VALID: `new(\"string_literal\")`, `new(123)`, `new(true)`\n  - DO NOT suggest replacing these with helper functions like `ptr.To()`.\n","AGENTS.md":"# Project Overview\nkgateway is a control plane implementing the Kubernetes Gateway API for Envoy. It's built on KRT (Kubernetes Declarative Controller Runtime from Istio) and uses a plugin-based architecture for extensibility.\n\n## Architecture\n\n### Translation Pipeline\nTranslation runs in 3 phases: Policy → IR, then HTTPRoute/Gateway → IR with policies attached, then IR → xDS. See `/devel/architecture/overview.md` and the translation diagram at `/devel/architecture/translation.svg`.\n\n### Plugin System\nkgateway translates Kubernetes Gateway API resources into Envoy configuration. Plugins *contribute* to that translation, usually by adding a new CRD (most commonly a Policy CRD) that users create to express their desired configuration. Policy CRDs attach to Gateway API resources via `targetRefs` or `targetSelectors`; kgateway manages the attachment during translation.\n\nConvert the CRD to an intermediate representation (IR) that is as close to Envoy protos as possible. This minimizes logic in the final translation and allows better status to be reported back to the user on errors.\n\nPlugins are **stateless across translations** but maintain state during a single gateway translation via `ProxyTranslationPass`. Each plugin provides a KRT collection of `ir.PolicyWrapper`, implements `NewGatewayTranslationPass`, and may hook backend processing — see `pkg/pluginsdk` for the interfaces.\n\nExample: `/pkg/kgateway/extensions2/plugins/trafficpolicy/traffic_policy_plugin.go`\n\n## Critical Developer Patterns\n\n### go build tag e2e\nIf you intend to include all source code, run `go` commands that accept `-tags` with `-tags e2e`.\n\n### IR Equals() Methods (STRICTLY ENFORCED)\nIRs output by KRT collections **must** implement `Equals(other T) bool`:\n- **Compare ALL fields** or mark with `// +noKrtEquals` (last line of comment)\n- `+krtEqualsTODO` exists only to track legacy gaps — never use it in new code\n- **Never use `reflect.DeepEqual`** — flagged by the custom `krtequals` analyzer (external module wired up in `.custom-gcl.yml`, configured in `.golangci.yaml`)\n- Use proto equality helpers: `proto.Equal()`, not `==`\n- Unit test the `Equals` method\n\nHigh-risk area: an incomplete `Equals` silently breaks KRT change detection.\n\n### Code Generation Workflow\n`make help` lists the codegen targets. `make generate-all` is the usual choice (stamp-based, only regenerates what changed); `make generated-code` force-regenerates everything. Always run `make fmt` or `make fmt-changed` before committing.\n\nAfter API changes: run `make go-generate-apis` then `make fmt-changed`. Dependency tracking lives in `_output/stamps/`; run `make clean-stamps` if regeneration seems stuck. If not sure, just run `make generate-all`.\n\n### Testing Conventions\n- **Unit tests**: For new code, avoid Ginkgo. You may use Gomega matchers if appropriate.\n- **E2E tests**: Use framework in `/test/e2e/` - DO NOT directly kubectl apply in tests\n- **Custom matchers**: `/test/gomega/matchers/` (e.g., `HaveHttpResponse`)\n- **Transforms**: Compose matchers with `WithTransform()` (see `/devel/testing/writing-tests.md`)\n- Prefer explicit error checking: `Expect(err).To(MatchError(\"msg\"))` over `HaveOccurred()`\n- Add descriptions: `Expect(x).To(BeEmpty(), \"list should be empty on init\")`\n- Never manually delete e2e resources in a specific order - let the framework handle teardown\n\n```bash\nmake test TEST_PKG=./path/to/package        # Unit tests\nmake e2e-test TEST_PKG=./test/e2e/tests/... # E2E tests\nmake unit                                   # All unit tests (excludes e2e)\n```\n\n### API/CRD Development\n\nRead the relevant doc BEFORE starting either of these — both have non-obvious required steps:\n- **Adding a new CRD**: `/api/README.md` (field conventions, codegen, and the client + fake-client registration steps).\n- **Adding a field to an existing Policy CRD**: `/devel/contributing/adding-policy-fields.md` (IR translation, golden-file refresh via `REFRESH_GOLDEN=true`, E2E registration).\n\nKey constraint either way: translate as close to the Envoy protos as possible in the IR, not in the translation pass — the translation pass should stay very lightweight.\n\n## Local Development\n```bash\nmake run                    # kind + CRDs + MetalLB + images + charts\nmake kind-reload-kgateway   # Rebuild, load, and restart after a code change\n\nmake conformance                               # Gateway API conformance\nmake all-conformance                           # All suites\nmake conformance-HTTPRouteSimpleSameNamespace  # Specific test by ShortName\n```\n\nPinned versions live in the `Makefile` (`ENVOY_IMAGE`, `ALPINE_BASE_IMAGE`, and `CONFORMANCE_VERSION` for the Gateway API CRDs); bump Gateway API with `make bump-gtw DEP_REF=v1.3.0`. Run `make help` for all targets.\n\n## Opening Pull Requests\n\n### Before you open\n1. `make verify` - regenerates code and fails if it produces a local diff\n2. `make analyze` - runs the linter\n3. `make lint-actions` - only if you modified files in `.github/`\n4. Every commit needs a `Signed-off-by` trailer ([DCO](https://developercertificate.org/) is a required check). Use `git commit -s`.\n\n### PR body\nPRs must follow `/.github/PULL_REQUEST_TEMPLATE.md`, which GitHub pre-fills in the web UI. **`gh pr create --body`/`--body-file` bypasses the template**, so when opening a PR from the CLI you must reproduce the structure yourself. The `labeler` workflow parses the body and is a **required check** — a missing change type or changelog block fails CI.\n\nFull details: `/devel/contributing/pull-requests.md` and `/.github/workflows/README.md`.\n\n## Further Reading\nDocs not already linked above: `/devel/contributing/README.md` (contributing overview), `/devel/contributing/conventions.md` (coding conventions), `/devel/contributing/code-generation.md` (codegen internals).\n","CLAUDE.md":"AGENTS.md","pkg/kgateway/extensions2/plugins/waypoint/README.md":"# Kgateway Istio Waypoint Integration\n\n**Note:** This document serves as a reference for some of key design decisions related to Kgateway Istio Waypoint integration. Rather than creating extensive documentation for each decision, this file collects important decision points to provide a centralized reference.\n\n## Authorization Policy Namespace Configuration\n\n### Overview\nThis directory contains the implementation for integrating Kgateway with Istio Waypoints, including support for waypoint authorization policies through GatewayClass as a TargetRef.\n\n### Root Namespace for Authorization Policies\nWhen implementing GatewayClass as a TargetRef for waypoint authorization policies, we needed to determine which namespace(s) should be used for global policies. After discussion among maintainers, the following decisions were made:\n\n1. **Default Root Namespace**: The default root namespace for waypoint authorization policies will be `istio-system` to maintain consistency with Istio's implementation.\n\n2. **Configurability**: The root namespace is configurable through Settings, allowing users to change it if needed.\n\n3. **Setting Name**: The configuration setting will be called \"Mesh root namespace\" to clearly indicate its purpose.\n\n### Rationale\n- Maintaining consistency with Istio's implementation reduces confusion for users, especially during migration.\n- The `istio-system` namespace is typically the permanent root namespace for mesh configurations.\n- Waypoint configurations should be consistent with ztunnel regardless of whether using Istio or kgateway.\n- Having a single configurable namespace (rather than multiple namespaces) avoids potential conflicts between duplicate authorization policies.\n\n### Usage\nAuthorization policies targeting waypoints should be placed in the `istio-system` namespace by default, or in the configured mesh root namespace if changed via Settings.\n\n```yaml\n# Example authorization policy in istio-system namespace\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: waypoint-policy\n  namespace: istio-system\nspec:\n  targetRef:\n    kind: GatewayClass\n    name: kgateway-waypoint\n  # Policy details...\n```\n\nFor users migrating from Istio to KGateway, using the default `istio-system` namespace simplifies the transition by allowing existing authorization policies to work with KGateway waypoints.","tools/build-tools/README.md":"## build-tools (Codespaces/devcontainer image)\n\nThis directory contains the Docker image definition for the `build-tools` devcontainer used by this repo.\n\nIt is **inspired by Istio's `build-tools` image** (from `istio/common-files`) and is intended to be\npublished to GitHub Container Registry (GHCR) so GitHub Codespaces can pull it quickly and reliably.\n\n### What’s included (high level)\n\n- Go (version matches `go.mod`)\n- Rust toolchain (for `internal/envoy_modules/`)\n- Common build tooling: `git`, `make`, `gcc`, `jq`, `yq`, `kubectl`, `kind`, `helm`, `protoc`, `buf`\n- Docker CLI (for `docker-outside-of-docker` feature)\n- `vim` (for editing) \n\n### Building locally\n\nYou can build the image locally with the make target:\n```bash\nmake build-tools-image\n```\n\nTest the build container locally:\n```shell\ndocker run -it -v \"$(pwd):/workspace\" -w /workspace kgateway-build-tools:dev\n```\n\nYou should be able to run kgateway commands (ie. `make generate-all`, `make run`, etc.) from within the container.\n\n### Testing as GitHub Codespaces would build it\n\nGitHub Codespaces uses the `@devcontainers/cli` to build and run the devcontainer. To reproduce\nexactly what Codespaces does (Dockerfile + devcontainer features like `docker-outside-of-docker`\nand `kind`), use the `devcontainer` CLI:\n\n```bash\n# Install the CLI (one-time)\nnpm install -g @devcontainers/cli\n\n# Build the image from the repo root (this is what Codespaces runs)\ndevcontainer build --workspace-folder .\n```\n\nThis builds the Dockerfile **and** installs the devcontainer features defined in\n`.devcontainer/devcontainer.json`, producing the same image a Codespace would use.\n\nTo run an interactive shell in the built image:\n```bash\ndevcontainer up --workspace-folder .\ndevcontainer exec --workspace-folder . bash\n```\n\n> **Note:** `make build-tools-image` only builds the Dockerfile and does **not** apply\n> devcontainer features. Use `devcontainer build` when you need to verify that features\n> (Docker CLI version pinning, kind, etc.) are configured correctly.\n\n### Using the devcontainer with VS Code\n\n1. Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.\n2. Open the root of this repo in VS Code.\n3. Click the \"Reopen in Container\" button.\n\nYou should now be able run kgateway commands inside the devcontainer instead of having to install dependencies locally.\n"},"files":{".claude/skills/cve-bump/SKILL.md":"---\nname: cve-bump\ndescription: Patch CVEs surfaced by the kgateway OSV scanner by bumping the affected Go module dependencies (direct, indirect via require, or replace) and curating false-positive entries in osv-scanner.toml. Use this skill whenever the user wants to fix CVEs, address OSV-Scanner alerts, bump deps for security reasons, triage code-scanning findings, \"see what CVEs we have\", or do anything CVE/security-bump-flavored in this repo — even if they don't name the skill explicitly.\n---\n\n# cve-bump\n\nEnd-to-end workflow for clearing CVEs in kgateway's Go dependencies. The user\nwants a single pass: discover the alerts → decide the action for each →\npresent a plan → apply the bumps and ignore-list updates → verify locally.\n\n## Why this skill exists\n\nCVE patching in kgateway is more mechanical than it looks, but the moving\nparts are easy to miss:\n\n- The scanner findings live in GitHub code-scanning, not the local tree.\n  `hack/osvtool` is the canonical way to fetch them.\n- Findings are uploaded per-branch and only the branches allowlisted in\n  the OSV scan workflow (`.github/workflows/osv-scanner.yaml`) get\n  scanned, so a topic branch has to query its base branch.\n- Some \"vulnerabilities\" are scanner false positives caused by\n  pseudo-version comparison quirks (see the istio.io entries in\n  `osv-scanner.toml`). These belong in the ignore list, not in go.mod\n  churn.\n- Indirect deps need either a `go get` of the parent that pulls in the\n  fixed version, or an explicit `require` to force MVS to pick the fixed\n  version. Getting this wrong looks like the bump \"didn't take.\"\n\nThe skill encodes those decisions so the human only has to approve the\nplan, not relearn the rules each time.\n\n## Protected dependencies\n\nSome dependencies are load-bearing enough that bumping them risks\nbreaking the dataplane, compatibility with managed Kubernetes\nversions, or the Gateway API conformance contract. **Never bump any\nof these without an explicit, in-conversation yes from the user —\neven for a critical CVE.**\n\n- `github.com/envoyproxy/*` (Envoy and `go-control-plane`)\n- `k8s.io/*` (api, apimachinery, client-go, ...)\n- `istio.io/*`\n- `sigs.k8s.io/gateway-api` and other Gateway API modules\n\nHow this gets enforced through the workflow:\n\n- In the plan (phase 4), surface protected-dep rows with the action\n  `needs approval (protected dep)` and list them in their own\n  bucket. Do not fold them into a batch `go get`.\n- In the execute step (phase 5), confirm each protected-dep bump\n  one-by-one before running `go get`. If the user says no, leave\n  the dep alone and list the CVE under \"deferred — protected dep\"\n  in the report.\n- Never silence a protected-dep CVE via `osv-scanner.toml` to avoid\n  asking. The ignore list is for false positives only.\n\nIf a transitive bump (parent dep) would pull a new version of a\nprotected dep through MVS, that still counts as bumping the\nprotected dep. Detect it (`go list -m -json <module>` after the\nparent bump) and bring it back to the user.\n\n## CVEs with no known fix\n\nOSV-Scanner sometimes surfaces advisories that have no fixed\nversion yet (or were withdrawn upstream). Do not:\n\n- Pin to a fork or a personal patch.\n- Add an `osv-scanner.toml` ignore entry just to clear the alert.\n- Invent a \"mitigation\" not stated in the advisory.\n\nInstead, collect these into a \"no fix available\" list during phase\n4 and surface them to the user verbatim in the report (phase 7):\nCVE / severity / module / pinned version / advisory URL. They are\ntracked, not patched.\n\n## Trigger fit\n\nStrong fit when the user says any of:\n\n- \"patch the CVEs\", \"fix the CVEs\", \"clear the OSV alerts\"\n- \"bump X for the CVE\", \"address GHSA-...\", \"what CVEs do we have?\"\n- \"scanner is complaining about Y\", \"osv-scanner findings\"\n- \"update deps for security\"\n\nSkip when the user wants a non-security dep bump (no CVE motivation) —\n`hack/bump_deps.sh` and a plain `go get` are usually right for those.\n\n## Tooling\n\nThis is a Go shop. The repo does not ship a Python toolchain and many\ncontributors do not have one installed — `pip install pyyaml` is **not**\na viable step.\n\nFor every parsing, filtering, or shaping task in this workflow, use:\n\n- **`jq`** for JSON. Required by `hack/osvtool` already, so present\n  whenever osvtool runs.\n- **`yq`** for YAML. Also required by `hack/osvtool`. Use\n  `yq -p=yaml -o=json` to convert the osvtool YAML back into JSON so\n  `jq` can do the heavy lifting.\n- **`go`**, `make`, standard shell builtins.\n\nDo **not** reach for `python`, `python3`, `node`, `ruby`, or\n`pip`/`npm install <anything>`. If you find yourself wanting one, write\na `jq` expression instead — every shape in the osvtool output (alerts,\nrule metadata, message text, help tables) is straightforward to handle\nin jq.\n\nConcrete starter: to round-trip the saved YAML back to a JSON array of\nalerts and start filtering:\n\n```bash\nyq -p=yaml -o=json /tmp/cve-bump/alerts-<branch>-<ts>.yaml \\\n  | jq '[ .[] | {\n      ghsa: .rule.id,\n      sev:  .rule.security_severity_level,\n      msg:  .most_recent_instance.message.text,\n      help: .rule.help,\n      url:  .html_url\n    } ]'\n```\n\nCVE IDs typically appear in `rule.full_description` or in the message\ntext alongside the package and version; the `help` field contains the\nGO-XXXX-XXXX advisory table with fixed versions. Extract with `jq`'s\n`capture` / regex helpers or a small `sed`/`grep` pipeline, not Python.\n\n## Workflow\n\nThere are six phases. Do them in order; do not skip the planning phase\neven if there is only one finding.\n\n### 1. Show the landscape (cross-branch summary)\n\nBefore pulling raw alerts, run the table mode across all supported\nbranches **for both targets** so the user sees source and image\nfindings side by side. With no `--branch` flags, `osvtool` scans\nexactly the branches allowlisted in the OSV scan workflow\n(`.github/workflows/osv-scanner.yaml`), so this stays correct as LTS\nbranches are cut or retired:\n\n```bash\n# Source-target landscape (the planning input for this skill)\n./hack/osvtool --table --target source --state open\n\n# Image-target landscape (will be revisited in phase 8)\n./hack/osvtool --table --target image --state open\n```\n\nEach call emits a compact markdown table — one row per branch,\ncolumns: `branch | C | H | M | L | max age | last scan |\ncode-scanning`. Show both to the user as-is; together they fit in\none screen and make image vs. source scope explicit.\n\nUse `--state open` (not `todo`) here so the medium/low counts are\nvisible. The user is about to decide whether to include them; they\nshould see the numbers first.\n\n**Do not** use the default YAML summary mode (without `--table`) for\nthis — it emits ~60 lines per branch of nested fields the user does\nnot need. If you ever want the full structured form (e.g. to grep a\nspecific field), pipe it to a file under `/tmp/cve-bump/` instead of\ninto the chat.\n\n### 2. Decide severity scope and target branch\n\nAsk the user two short questions, back to back:\n\n1. **Severity scope**: just critical/high (`--state todo`), or all open\n   severities including medium/low (`--state open`)? Recommend\n   critical/high since that matches how teams typically prioritize, but\n   the landscape view from phase 1 shows the full severity breakdown so\n   the user can make an informed call (e.g. if there are 30 mediums on\n   `v2.2.x` that they want to clear ahead of a release). Just ask in\n   one sentence — no menu.\n\n2. **Branch to plan against**: the raw pull is single-branch because\n   planning depends on that branch's go.mod state. Run the detection\n   helper for a default:\n\n   ```bash\n   .claude/skills/cve-bump/scripts/detect_base.sh\n   ```\n\n   It prints one line — the supported branch the current HEAD is most\n   likely based on (most-recent merge-base across the branches the OSV\n   scan workflow allowlists, read from\n   `.github/workflows/osv-scanner.yaml`), or `main` if it cannot\n   decide. Confirm with the user: \"Planning against `<X>`. OK, or pick\n   another?\"\n\nIf the landscape view in phase 1 showed zero alerts at the chosen\nseverity for the chosen branch, stop here and tell the user — there is\nnothing to plan.\n\n### 3. Pull the raw alerts\n\n```bash\n./hack/osvtool --raw --branch <branch> --target source --state <state>\n```\n\nWhere `<state>` is whatever the user picked in phase 2 (`todo` or\n`open`).\n\nNotes:\n\n- `--target source` filters this pass to Go-mod findings only.\n  Image findings are addressed separately in phase 8 because their\n  actions are different (Dockerfile / base-image / apk changes) and\n  many of them resolve transitively once the source bumps land.\n  Do not try to plan them in the same table.\n- The output is YAML. Save it to a scratch path outside the repo so it\n  is not accidentally committed. Use\n  `/tmp/cve-bump/alerts-<branch>-<UTC-timestamp>.yaml` (mkdir -p first).\n  This gives the user a record and lets us re-parse without another\n  GitHub call.\n\nIf `osvtool` errors with an auth message, surface the help text it\nprints — it already explains the fix (`gh auth refresh --scopes\nsecurity_events` or a fine-grained PAT). Do not paper over it.\n\n### 4. Plan one action per finding\n\nFor each alert, extract:\n\n- CVE ID (`rule.id` is typically the GHSA; the CVE lives in\n  `rule.full_description` or `most_recent_instance.message.text`).\n- Severity (`rule.security_severity_level`).\n- Affected Go module + current pinned version.\n- Fixed version (the lowest version that resolves the advisory).\n  May be **absent** — see the no-fix branch below.\n- Whether the module is direct or indirect in this repo's go.mod.\n- Whether the module is a [protected dependency](#protected-dependencies).\n\nThen pick an action per the decision tree below.\n\n#### Decision tree\n\n```mermaid\nflowchart TD\n    A[Finding] --> N{Is there a fixed version<br/>in the advisory?}\n    N -- no --> NF[Report only:<br/>add to \"no fix available\" list]\n    N -- yes --> P{Is the module a protected dep<br/>envoy / k8s.io/* / istio.io/* / gateway-api?}\n    P -- yes --> PA[Needs approval:<br/>row in plan with action \"needs approval protected dep\"<br/>do not include in batch go get]\n    P -- no --> B{Is the pinned version actually >= fix,<br/>once you account for pseudo-versions?}\n    B -- yes --> C[False positive → add to osv-scanner.toml]\n    B -- no --> D{Is the module direct in go.mod?}\n    D -- yes --> E[go get module@fixed-version]\n    D -- no --> F{Does a single parent dep<br/>own this transitive?}\n    F -- yes, and bumping it is reasonable --> G[go get parent@version-that-pulls-fix]\n    F -- no, or parent bump too risky --> H[Add explicit require<br/>go get module@fixed-version<br/>force MVS to pick the fix]\n```\n\n**Detecting pseudo-version false positives**: when the affected version\nlooks like `v0.0.0-<14-digit-timestamp>-<sha>` and the fix version is a\nnormal semver like `1.x`, OSV-Scanner compares them lexically as\n`\"0.0.0\" < \"1.x\"` and fires regardless of whether the pinned commit\nalready includes the fix. Read the timestamp embedded in the\npseudo-version and compare to the fix release date. If the pinned\ncommit is newer, it is a false positive — add to `osv-scanner.toml`\nwith a reason that names the timestamp, the fix release date, and the\nyears/months gap. The five istio.io entries already in that file are\nthe model to copy.\n\n**Choosing between parent bump and explicit require**: prefer the\nparent bump when (a) the parent is itself behind by only a minor/patch\nversion, and (b) its changelog does not flag breaking changes for our\nusage. Otherwise add an explicit require — it is the smallest possible\nintervention and Go's MVS will honor it.\n\n#### Output: the plan\n\nPresent the plan as up to three fenced tables so each bucket is\nvisually distinct.\n\n**1. Actionable bumps** — what you intend to `go get` or ignore.\nColumns: `CVE | severity | module | from → to | action`.\n\n```\n| CVE             | sev | module                | from           | to        | action                |\n| --------------- | --- | --------------------- | -------------- | --------- | --------------------- |\n| CVE-2025-1111   | H   | golang.org/x/net      | v0.20.0        | v0.27.0   | go get (direct)       |\n| CVE-2025-2222   | M   | gopkg.in/foo.v3       | v3.1.0         | v3.2.1    | explicit require      |\n| CVE-2022-31045  | H   | istio.io/istio        | v0.0.0-2026... | (n/a)     | ignore: false pos     |\n```\n\n**2. Needs approval — protected deps** (omit the table if empty).\nColumns: `CVE | severity | module | from → to | reason`. The user\nmust say yes per row before any of these move into the actionable\nbatch.\n\n```\n| CVE             | sev | module                  | from     | to       | reason                  |\n| --------------- | --- | ----------------------- | -------- | -------- | ----------------------- |\n| CVE-2025-9999   | H   | k8s.io/apimachinery     | v0.31.2  | v0.31.5  | only fix is a k8s.io bump |\n```\n\n**3. No fix available** (omit the table if empty). Columns:\n`CVE | severity | module | pinned | advisory`. Read-only — no\naction proposed. These will be repeated verbatim in the final\nreport.\n\n```\n| CVE             | sev | module                | pinned    | advisory                                        |\n| --------------- | --- | --------------------- | --------- | ----------------------------------------------- |\n| CVE-2025-7777   | M   | example.com/foo       | v1.4.0    | https://github.com/advisories/GHSA-xxxx-yyyy-zz |\n```\n\nThen ask the user to OK the actionable table, decide each\nprotected-dep row, and acknowledge the no-fix list. Wait for\nexplicit approval before touching files.\n\n### 5. Execute\n\nAfter approval, in order:\n\n1. **Protected-dep gate.** For any row whose action was `needs\n   approval (protected dep)`, only proceed if the user explicitly\n   said yes to that row. If unclear, ask again. Do not batch\n   protected deps into step 2 — `go get` them one at a time so the\n   user can stop the flow if a transitive surprise shows up.\n2. For each remaining `go get` action: `go get <module>@<version>`.\n3. `go mod tidy` once at the end.\n4. **Check for surprise protected-dep transitives.** After\n   `go mod tidy`, diff `go.mod` for any envoy / k8s.io / istio.io /\n   gateway-api lines that changed and were not in the approved\n   plan. If any did, stop, surface them to the user, and let them\n   decide (revert or approve).\n5. For each ignore action: append a `[[IgnoredVulns]]` entry to\n   `osv-scanner.toml`. The `reason` must be specific — name the\n   pseudo-version timestamp, the fix release date, and the gap. Do not\n   write generic reasons like \"false positive\" or \"not applicable\".\n   Never add an ignore entry for a no-fix CVE or a protected-dep\n   CVE just to clear noise.\n6. If `go.sum` changed, run `make generate-licenses` to refresh the\n   license attribution files.\n7. Run `make fmt-changed` to keep the diff clean.\n\nDo not stage anything (`git add`) and do not commit. The user reviews\nthe dirty tree themselves.\n\n### 6. Verify\n\nThe user signed off on \"build + scanner re-run only\", so:\n\n1. `go build -tags e2e ./...` — the `e2e` tag is required to compile\n   all source per the repo's CLAUDE.md. If the build fails, the bump is\n   not safe; stop and report.\n2. Local scanner re-run: **`make osv-scan`**. This is the canonical\n   way to run OSV-Scanner locally in kgateway — it shells out to\n   Docker (image `ghcr.io/google/osv-scanner-action`), so you do not\n   need a local `osv-scanner` binary. Do **not** reach for a\n   standalone `osv-scanner` invocation; the make target handles config\n   lookup, output paths, and reporter conversion in one shot.\n   - Results land at `_output/osv/<safe-branch>/results.json` (and a\n     SARIF sibling). `<safe-branch>` is the current branch with `/` and\n     `.` replaced by `-`.\n   - The `make osv-scan` JSON schema is osv-scanner native\n     (`results[].packages[].vulnerabilities[].id`), which is different\n     from the GitHub code-scanning YAML from phase 3. To diff, extract\n     the set of vuln IDs from each side with `jq`/`yq` and compare the\n     sets — don't try to align records field-by-field. Report which\n     CVEs the user expected to clear that still appear, and any new\n     ones the scanner surfaced that were not in the original plan.\n3. If Docker is not running, `make osv-scan` will fail with a Docker\n   error. Tell the user and stop — do not try to install osv-scanner\n   from source as a workaround.\n\n### 7. Report\n\nEnd with the following sections, in order:\n\n1. **What changed.** One line per CVE that got an action: CVE,\n   module, from → to (or \"ignore: <reason summary>\"), and the\n   file(s) touched. The user reads this against `git diff`.\n2. **Deferred — protected dep.** Any protected-dep rows the user\n   said no to (or did not respond to). List CVE / module / proposed\n   version / why deferred so the next maintainer has the context.\n3. **No fix available.** Verbatim from the phase-4 table — CVE /\n   severity / module / pinned / advisory URL. Do not propose\n   workarounds. Do not add `osv-scanner.toml` entries for these.\n\nThen continue into phase 8.\n\n### 8. Image-target follow-up\n\nImage findings often resolve transitively once source bumps land\n(e.g. a Go binary baked into the image gets the fixed dep), but\nsome do not — base-image CVEs and apk packages need Dockerfile or\nbase-image changes.\n\nRefresh the image landscape against the planned branch:\n\n```bash\n./hack/osvtool --table \\\n    --branch <branch> \\\n    --target image --state open\n```\n\nThen prompt the user: \"The source bumps are done. Image findings\nabove remain on the published scan — most should clear once these\nbumps ship, but base-image / apk CVEs need a Dockerfile change.\nWant to plan that now, or defer?\"\n\nTwo important caveats to state plainly:\n\n- The `--table` view reflects the **last GitHub-uploaded scan**,\n  not the working tree. The source bumps you just made won't be\n  reflected in image scans until CI rebuilds and republishes\n  results.\n- A local image scan is possible via\n  `make osv-scan OSV_SCAN_IMAGES=\"<image-ref> ...\"` (see\n  `osv-scan-latest-main-images` in the Makefile for the canonical\n  invocation against rolling-main images), but that scans\n  **already-built published images**, not the just-bumped working\n  tree. Mention this if the user asks why a \"fixed\" CVE still\n  appears locally.\n\nIf the user says \"defer\", stop here. If they say \"go\", treat the\nimage flow as a separate planning round — start a new plan with\nDockerfile-level actions (do not extend the Go-mod plan with image\nactions, they live in different files and the decision tree above\ndoes not apply).\n\n## Edge cases worth knowing\n\n- **Pseudo-version on the fix side**: rare, but if a fix only exists at\n  a non-tagged commit, `go get module@<sha>` works. Mark this clearly\n  in the plan so the user is not surprised by a pseudo-version in\n  go.mod.\n- **Module renamed/moved**: if the advisory's module path no longer\n  exists upstream, a `replace` directive may be necessary. Treat this\n  as ambiguous and flag it for the user instead of writing the replace\n  silently.\n- **Multiple CVEs on one module**: combine into a single `go get` to\n  the highest required version. List them as separate rows in the plan\n  table but mark \"(combined bump)\" in the action column.\n- **Backport branches**: when scanning `v2.2.x` etc., the supported\n  upgrade window for a given module may be narrower (e.g. cannot move\n  to a new major). Default to the smallest version that resolves the\n  CVE within the same major.\n- **Topic branch with no alerts on the base branch**: possible if the\n  branch was just rebased after a recent scan. Tell the user; offer to\n  re-run after CI uploads fresh results.\n\n## Files this skill touches\n\n- `go.mod` / `go.sum` — version bumps\n- `osv-scanner.toml` — false-positive ignore entries (root-level; this\n  is the only scanner ignore file in this repo as of writing — `.snyk`,\n  `.grype.yaml`, `.trivyignore` are not present)\n- License attribution files, if `make generate-licenses` is wired up\n\n## Bundled scripts\n\n- `scripts/detect_base.sh` — prints the supported branch that the\n  current HEAD is most likely based on. Used in phase 1.\n\n## Things not to do\n\n- Do **not** commit or stage. The user does that.\n- Do **not** edit go.mod by hand — always go through `go get` so\n  go.sum stays consistent.\n- Do **not** bump a [protected dependency](#protected-dependencies)\n  without an explicit, in-conversation yes from the user — not in a\n  batch, not as a transitive pulled in by a parent bump. Surface it\n  for approval and wait.\n- Do **not** invent a workaround for a CVE with no upstream fix.\n  Do not pin to a fork, do not silence it via `osv-scanner.toml`.\n  List it in the no-fix bucket and move on.\n- Do **not** stop after the source bumps — phase 8 (image\n  follow-up) is part of the workflow. Prompt the user about\n  remaining image findings every time.\n- Do **not** add an `osv-scanner.toml` entry just to silence noise.\n  The reason field has to defend the entry to a future reader.\n- Do **not** run `make generate-all` or other heavy codegen targets\n  for a dep bump — they are not needed for non-API changes and they\n  slow the loop down by ~30s for no benefit.\n- Do **not** scan an unsupported branch (e.g. `chandler/cveskill`)\n  directly via `osvtool` — there are no GitHub-uploaded alerts for it.\n  (Note: `make osv-scan` does work on any branch because it runs the\n  scanner locally; that is fine for verification.)\n- Do **not** reach for Python, Node, or any other parser. Use `jq`\n  and `yq` — see the Tooling section.\n- Do **not** invoke `osv-scanner` directly. Use `make osv-scan` — it\n  runs the scanner in Docker with the right config and output paths.\n- Do **not** dump the default YAML summary into the chat for the\n  cross-branch landscape view. Use `./hack/osvtool --table` (phase 1)\n  — it's a single small markdown table instead of dozens of lines of\n  nested YAML.\n",".github/copilot-instructions.md":"# Go Version\nThis project uses **Go 1.26**.\n\n# Syntax Overrides\nThe following syntax is valid in Go 1.26 and must NOT be flagged as an error:\n- `new(value)`: The `new()` function now accepts expressions/values, not just types.\n  - VALID: `new(\"string_literal\")`, `new(123)`, `new(true)`\n  - DO NOT suggest replacing these with helper functions like `ptr.To()`.\n","AGENTS.md":"# Project Overview\nkgateway is a control plane implementing the Kubernetes Gateway API for Envoy. It's built on KRT (Kubernetes Declarative Controller Runtime from Istio) and uses a plugin-based architecture for extensibility.\n\n## Architecture\n\n### Translation Pipeline\nTranslation runs in 3 phases: Policy → IR, then HTTPRoute/Gateway → IR with policies attached, then IR → xDS. See `/devel/architecture/overview.md` and the translation diagram at `/devel/architecture/translation.svg`.\n\n### Plugin System\nkgateway translates Kubernetes Gateway API resources into Envoy configuration. Plugins *contribute* to that translation, usually by adding a new CRD (most commonly a Policy CRD) that users create to express their desired configuration. Policy CRDs attach to Gateway API resources via `targetRefs` or `targetSelectors`; kgateway manages the attachment during translation.\n\nConvert the CRD to an intermediate representation (IR) that is as close to Envoy protos as possible. This minimizes logic in the final translation and allows better status to be reported back to the user on errors.\n\nPlugins are **stateless across translations** but maintain state during a single gateway translation via `ProxyTranslationPass`. Each plugin provides a KRT collection of `ir.PolicyWrapper`, implements `NewGatewayTranslationPass`, and may hook backend processing — see `pkg/pluginsdk` for the interfaces.\n\nExample: `/pkg/kgateway/extensions2/plugins/trafficpolicy/traffic_policy_plugin.go`\n\n## Critical Developer Patterns\n\n### go build tag e2e\nIf you intend to include all source code, run `go` commands that accept `-tags` with `-tags e2e`.\n\n### IR Equals() Methods (STRICTLY ENFORCED)\nIRs output by KRT collections **must** implement `Equals(other T) bool`:\n- **Compare ALL fields** or mark with `// +noKrtEquals` (last line of comment)\n- `+krtEqualsTODO` exists only to track legacy gaps — never use it in new code\n- **Never use `reflect.DeepEqual`** — flagged by the custom `krtequals` analyzer (external module wired up in `.custom-gcl.yml`, configured in `.golangci.yaml`)\n- Use proto equality helpers: `proto.Equal()`, not `==`\n- Unit test the `Equals` method\n\nHigh-risk area: an incomplete `Equals` silently breaks KRT change detection.\n\n### Code Generation Workflow\n`make help` lists the codegen targets. `make generate-all` is the usual choice (stamp-based, only regenerates what changed); `make generated-code` force-regenerates everything. Always run `make fmt` or `make fmt-changed` before committing.\n\nAfter API changes: run `make go-generate-apis` then `make fmt-changed`. Dependency tracking lives in `_output/stamps/`; run `make clean-stamps` if regeneration seems stuck. If not sure, just run `make generate-all`.\n\n### Testing Conventions\n- **Unit tests**: For new code, avoid Ginkgo. You may use Gomega matchers if appropriate.\n- **E2E tests**: Use framework in `/test/e2e/` - DO NOT directly kubectl apply in tests\n- **Custom matchers**: `/test/gomega/matchers/` (e.g., `HaveHttpResponse`)\n- **Transforms**: Compose matchers with `WithTransform()` (see `/devel/testing/writing-tests.md`)\n- Prefer explicit error checking: `Expect(err).To(MatchError(\"msg\"))` over `HaveOccurred()`\n- Add descriptions: `Expect(x).To(BeEmpty(), \"list should be empty on init\")`\n- Never manually delete e2e resources in a specific order - let the framework handle teardown\n\n```bash\nmake test TEST_PKG=./path/to/package        # Unit tests\nmake e2e-test TEST_PKG=./test/e2e/tests/... # E2E tests\nmake unit                                   # All unit tests (excludes e2e)\n```\n\n### API/CRD Development\n\nRead the relevant doc BEFORE starting either of these — both have non-obvious required steps:\n- **Adding a new CRD**: `/api/README.md` (field conventions, codegen, and the client + fake-client registration steps).\n- **Adding a field to an existing Policy CRD**: `/devel/contributing/adding-policy-fields.md` (IR translation, golden-file refresh via `REFRESH_GOLDEN=true`, E2E registration).\n\nKey constraint either way: translate as close to the Envoy protos as possible in the IR, not in the translation pass — the translation pass should stay very lightweight.\n\n## Local Development\n```bash\nmake run                    # kind + CRDs + MetalLB + images + charts\nmake kind-reload-kgateway   # Rebuild, load, and restart after a code change\n\nmake conformance                               # Gateway API conformance\nmake all-conformance                           # All suites\nmake conformance-HTTPRouteSimpleSameNamespace  # Specific test by ShortName\n```\n\nPinned versions live in the `Makefile` (`ENVOY_IMAGE`, `ALPINE_BASE_IMAGE`, and `CONFORMANCE_VERSION` for the Gateway API CRDs); bump Gateway API with `make bump-gtw DEP_REF=v1.3.0`. Run `make help` for all targets.\n\n## Opening Pull Requests\n\n### Before you open\n1. `make verify` - regenerates code and fails if it produces a local diff\n2. `make analyze` - runs the linter\n3. `make lint-actions` - only if you modified files in `.github/`\n4. Every commit needs a `Signed-off-by` trailer ([DCO](https://developercertificate.org/) is a required check). Use `git commit -s`.\n\n### PR body\nPRs must follow `/.github/PULL_REQUEST_TEMPLATE.md`, which GitHub pre-fills in the web UI. **`gh pr create --body`/`--body-file` bypasses the template**, so when opening a PR from the CLI you must reproduce the structure yourself. The `labeler` workflow parses the body and is a **required check** — a missing change type or changelog block fails CI.\n\nFull details: `/devel/contributing/pull-requests.md` and `/.github/workflows/README.md`.\n\n## Further Reading\nDocs not already linked above: `/devel/contributing/README.md` (contributing overview), `/devel/contributing/conventions.md` (coding conventions), `/devel/contributing/code-generation.md` (codegen internals).\n","CLAUDE.md":"AGENTS.md","pkg/kgateway/extensions2/plugins/waypoint/README.md":"# Kgateway Istio Waypoint Integration\n\n**Note:** This document serves as a reference for some of key design decisions related to Kgateway Istio Waypoint integration. Rather than creating extensive documentation for each decision, this file collects important decision points to provide a centralized reference.\n\n## Authorization Policy Namespace Configuration\n\n### Overview\nThis directory contains the implementation for integrating Kgateway with Istio Waypoints, including support for waypoint authorization policies through GatewayClass as a TargetRef.\n\n### Root Namespace for Authorization Policies\nWhen implementing GatewayClass as a TargetRef for waypoint authorization policies, we needed to determine which namespace(s) should be used for global policies. After discussion among maintainers, the following decisions were made:\n\n1. **Default Root Namespace**: The default root namespace for waypoint authorization policies will be `istio-system` to maintain consistency with Istio's implementation.\n\n2. **Configurability**: The root namespace is configurable through Settings, allowing users to change it if needed.\n\n3. **Setting Name**: The configuration setting will be called \"Mesh root namespace\" to clearly indicate its purpose.\n\n### Rationale\n- Maintaining consistency with Istio's implementation reduces confusion for users, especially during migration.\n- The `istio-system` namespace is typically the permanent root namespace for mesh configurations.\n- Waypoint configurations should be consistent with ztunnel regardless of whether using Istio or kgateway.\n- Having a single configurable namespace (rather than multiple namespaces) avoids potential conflicts between duplicate authorization policies.\n\n### Usage\nAuthorization policies targeting waypoints should be placed in the `istio-system` namespace by default, or in the configured mesh root namespace if changed via Settings.\n\n```yaml\n# Example authorization policy in istio-system namespace\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: waypoint-policy\n  namespace: istio-system\nspec:\n  targetRef:\n    kind: GatewayClass\n    name: kgateway-waypoint\n  # Policy details...\n```\n\nFor users migrating from Istio to KGateway, using the default `istio-system` namespace simplifies the transition by allowing existing authorization policies to work with KGateway waypoints.","tools/build-tools/README.md":"## build-tools (Codespaces/devcontainer image)\n\nThis directory contains the Docker image definition for the `build-tools` devcontainer used by this repo.\n\nIt is **inspired by Istio's `build-tools` image** (from `istio/common-files`) and is intended to be\npublished to GitHub Container Registry (GHCR) so GitHub Codespaces can pull it quickly and reliably.\n\n### What’s included (high level)\n\n- Go (version matches `go.mod`)\n- Rust toolchain (for `internal/envoy_modules/`)\n- Common build tooling: `git`, `make`, `gcc`, `jq`, `yq`, `kubectl`, `kind`, `helm`, `protoc`, `buf`\n- Docker CLI (for `docker-outside-of-docker` feature)\n- `vim` (for editing) \n\n### Building locally\n\nYou can build the image locally with the make target:\n```bash\nmake build-tools-image\n```\n\nTest the build container locally:\n```shell\ndocker run -it -v \"$(pwd):/workspace\" -w /workspace kgateway-build-tools:dev\n```\n\nYou should be able to run kgateway commands (ie. `make generate-all`, `make run`, etc.) from within the container.\n\n### Testing as GitHub Codespaces would build it\n\nGitHub Codespaces uses the `@devcontainers/cli` to build and run the devcontainer. To reproduce\nexactly what Codespaces does (Dockerfile + devcontainer features like `docker-outside-of-docker`\nand `kind`), use the `devcontainer` CLI:\n\n```bash\n# Install the CLI (one-time)\nnpm install -g @devcontainers/cli\n\n# Build the image from the repo root (this is what Codespaces runs)\ndevcontainer build --workspace-folder .\n```\n\nThis builds the Dockerfile **and** installs the devcontainer features defined in\n`.devcontainer/devcontainer.json`, producing the same image a Codespace would use.\n\nTo run an interactive shell in the built image:\n```bash\ndevcontainer up --workspace-folder .\ndevcontainer exec --workspace-folder . bash\n```\n\n> **Note:** `make build-tools-image` only builds the Dockerfile and does **not** apply\n> devcontainer features. Use `devcontainer build` when you need to verify that features\n> (Docker CLI version pinning, kind, etc.) are configured correctly.\n\n### Using the devcontainer with VS Code\n\n1. Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.\n2. Open the root of this repo in VS Code.\n3. Click the \"Reopen in Container\" button.\n\nYou should now be able run kgateway commands inside the devcontainer instead of having to install dependencies locally.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/kgateway-dev/kgateway/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Project Overview\nkgateway is a control plane implementing the Kubernetes Gateway API for Envoy. It's built on KRT (Kubernetes Declarative Controller Runtime from Istio) and uses a plugin-based architecture for extensibility.\n\n## Architecture\n\n### Translation Pipeline\nTranslation runs in 3 phases: Policy → IR, then HTTPRoute/Gateway → IR with policies attached, then IR → xDS. See `/devel/architecture/overview.md` and the translation diagram at `/devel/architecture/translation.svg`.\n\n### Plugin System\nkgateway translates Kubernetes Gateway API resources into Envoy configuration. Plugins *contribute* to that translation, usually by adding a new CRD (most commonly a Policy CRD) that users create to express their desired configuration. Policy CRDs attach to Gateway API resources via `targetRefs` or `targetSelectors`; kgateway manages the attachment during translation.\n\nConvert the CRD to an intermediate representation (IR) that is as close to Envoy protos as possible. This minimizes logic in the final translation and allows better status to be reported back to the user on errors.\n\nPlugins are **stateless across translations** but maintain state during a single gateway translation via `ProxyTranslationPass`. Each plugin provides a KRT collection of `ir.PolicyWrapper`, implements `NewGatewayTranslationPass`, and may hook backend processing — see `pkg/pluginsdk` for the interfaces.\n\nExample: `/pkg/kgateway/extensions2/plugins/trafficpolicy/traffic_policy_plugin.go`\n\n## Critical Developer Patterns\n\n### go build tag e2e\nIf you intend to include all source code, run `go` commands that accept `-tags` with `-tags e2e`.\n\n### IR Equals() Methods (STRICTLY ENFORCED)\nIRs output by KRT collections **must** implement `Equals(other T) bool`:\n- **Compare ALL fields** or mark with `// +noKrtEquals` (last line of comment)\n- `+krtEqualsTODO` exists only to track legacy gaps — never use it in new code\n- **Never use `reflect.DeepEqual`** — flagged by the custom `krtequals` analyzer (external module wired up in `.custom-gcl.yml`, configured in `.golangci.yaml`)\n- Use proto equality helpers: `proto.Equal()`, not `==`\n- Unit test the `Equals` method\n\nHigh-risk area: an incomplete `Equals` silently breaks KRT change detection.\n\n### Code Generation Workflow\n`make help` lists the codegen targets. `make generate-all` is the usual choice (stamp-based, only regenerates what changed); `make generated-code` force-regenerates everything. Always run `make fmt` or `make fmt-changed` before committing.\n\nAfter API changes: run `make go-generate-apis` then `make fmt-changed`. Dependency tracking lives in `_output/stamps/`; run `make clean-stamps` if regeneration seems stuck. If not sure, just run `make generate-all`.\n\n### Testing Conventions\n- **Unit tests**: For new code, avoid Ginkgo. You may use Gomega matchers if appropriate.\n- **E2E tests**: Use framework in `/test/e2e/` - DO NOT directly kubectl apply in tests\n- **Custom matchers**: `/test/gomega/matchers/` (e.g., `HaveHttpResponse`)\n- **Transforms**: Compose matchers with `WithTransform()` (see `/devel/testing/writing-tests.md`)\n- Prefer explicit error checking: `Expect(err).To(MatchError(\"msg\"))` over `HaveOccurred()`\n- Add descriptions: `Expect(x).To(BeEmpty(), \"list should be empty on init\")`\n- Never manually delete e2e resources in a specific order - let the framework handle teardown\n\n```bash\nmake test TEST_PKG=./path/to/package        # Unit tests\nmake e2e-test TEST_PKG=./test/e2e/tests/... # E2E tests\nmake unit                                   # All unit tests (excludes e2e)\n```\n\n### API/CRD Development\n\nRead the relevant doc BEFORE starting either of these — both have non-obvious required steps:\n- **Adding a new CRD**: `/api/README.md` (field conventions, codegen, and the client + fake-client registration steps).\n- **Adding a field to an existing Policy CRD**: `/devel/contributing/adding-policy-fields.md` (IR translation, golden-file refresh via `REFRESH_GOLDEN=true`, E2E registration).\n\nKey constraint either way: translate as close to the Envoy protos as possible in the IR, not in the translation pass — the translation pass should stay very lightweight.\n\n## Local Development\n```bash\nmake run                    # kind + CRDs + MetalLB + images + charts\nmake kind-reload-kgateway   # Rebuild, load, and restart after a code change\n\nmake conformance                               # Gateway API conformance\nmake all-conformance                           # All suites\nmake conformance-HTTPRouteSimpleSameNamespace  # Specific test by ShortName\n```\n\nPinned versions live in the `Makefile` (`ENVOY_IMAGE`, `ALPINE_BASE_IMAGE`, and `CONFORMANCE_VERSION` for the Gateway API CRDs); bump Gateway API with `make bump-gtw DEP_REF=v1.3.0`. Run `make help` for all targets.\n\n## Opening Pull Requests\n\n### Before you open\n1. `make verify` - regenerates code and fails if it produces a local diff\n2. `make analyze` - runs the linter\n3. `make lint-actions` - only if you modified files in `.github/`\n4. Every commit needs a `Signed-off-by` trailer ([DCO](https://developercertificate.org/) is a required check). Use `git commit -s`.\n\n### PR body\nPRs must follow `/.github/PULL_REQUEST_TEMPLATE.md`, which GitHub pre-fills in the web UI. **`gh pr create --body`/`--body-file` bypasses the template**, so when opening a PR from the CLI you must reproduce the structure yourself. The `labeler` workflow parses the body and is a **required check** — a missing change type or changelog block fails CI.\n\nFull details: `/devel/contributing/pull-requests.md` and `/.github/workflows/README.md`.\n\n## Further Reading\nDocs not already linked above: `/devel/contributing/README.md` (contributing overview), `/devel/contributing/conventions.md` (coding conventions), `/devel/contributing/code-generation.md` (codegen internals).\n","isInternal":false,"tokens":1378,"sizeBytes":5821},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/kgateway-dev/kgateway/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"AGENTS.md","isInternal":false,"tokens":3,"sizeBytes":9},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/kgateway-dev/kgateway/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"# Go Version\nThis project uses **Go 1.26**.\n\n# Syntax Overrides\nThe following syntax is valid in Go 1.26 and must NOT be flagged as an error:\n- `new(value)`: The `new()` function now accepts expressions/values, not just types.\n  - VALID: `new(\"string_literal\")`, `new(123)`, `new(true)`\n  - DO NOT suggest replacing these with helper functions like `ptr.To()`.\n","isInternal":false,"tokens":96,"sizeBytes":361},{"name":"SKILL.md","path":".claude/skills/cve-bump/SKILL.md","rawUrl":"https://raw.githubusercontent.com/kgateway-dev/kgateway/HEAD/.claude/skills/cve-bump/SKILL.md","title":"cve-bump","category":"anthropic-skill","format":"markdown","content":"---\nname: cve-bump\ndescription: Patch CVEs surfaced by the kgateway OSV scanner by bumping the affected Go module dependencies (direct, indirect via require, or replace) and curating false-positive entries in osv-scanner.toml. Use this skill whenever the user wants to fix CVEs, address OSV-Scanner alerts, bump deps for security reasons, triage code-scanning findings, \"see what CVEs we have\", or do anything CVE/security-bump-flavored in this repo — even if they don't name the skill explicitly.\n---\n\n# cve-bump\n\nEnd-to-end workflow for clearing CVEs in kgateway's Go dependencies. The user\nwants a single pass: discover the alerts → decide the action for each →\npresent a plan → apply the bumps and ignore-list updates → verify locally.\n\n## Why this skill exists\n\nCVE patching in kgateway is more mechanical than it looks, but the moving\nparts are easy to miss:\n\n- The scanner findings live in GitHub code-scanning, not the local tree.\n  `hack/osvtool` is the canonical way to fetch them.\n- Findings are uploaded per-branch and only the branches allowlisted in\n  the OSV scan workflow (`.github/workflows/osv-scanner.yaml`) get\n  scanned, so a topic branch has to query its base branch.\n- Some \"vulnerabilities\" are scanner false positives caused by\n  pseudo-version comparison quirks (see the istio.io entries in\n  `osv-scanner.toml`). These belong in the ignore list, not in go.mod\n  churn.\n- Indirect deps need either a `go get` of the parent that pulls in the\n  fixed version, or an explicit `require` to force MVS to pick the fixed\n  version. Getting this wrong looks like the bump \"didn't take.\"\n\nThe skill encodes those decisions so the human only has to approve the\nplan, not relearn the rules each time.\n\n## Protected dependencies\n\nSome dependencies are load-bearing enough that bumping them risks\nbreaking the dataplane, compatibility with managed Kubernetes\nversions, or the Gateway API conformance contract. **Never bump any\nof these without an explicit, in-conversation yes from the user —\neven for a critical CVE.**\n\n- `github.com/envoyproxy/*` (Envoy and `go-control-plane`)\n- `k8s.io/*` (api, apimachinery, client-go, ...)\n- `istio.io/*`\n- `sigs.k8s.io/gateway-api` and other Gateway API modules\n\nHow this gets enforced through the workflow:\n\n- In the plan (phase 4), surface protected-dep rows with the action\n  `needs approval (protected dep)` and list them in their own\n  bucket. Do not fold them into a batch `go get`.\n- In the execute step (phase 5), confirm each protected-dep bump\n  one-by-one before running `go get`. If the user says no, leave\n  the dep alone and list the CVE under \"deferred — protected dep\"\n  in the report.\n- Never silence a protected-dep CVE via `osv-scanner.toml` to avoid\n  asking. The ignore list is for false positives only.\n\nIf a transitive bump (parent dep) would pull a new version of a\nprotected dep through MVS, that still counts as bumping the\nprotected dep. Detect it (`go list -m -json <module>` after the\nparent bump) and bring it back to the user.\n\n## CVEs with no known fix\n\nOSV-Scanner sometimes surfaces advisories that have no fixed\nversion yet (or were withdrawn upstream). Do not:\n\n- Pin to a fork or a personal patch.\n- Add an `osv-scanner.toml` ignore entry just to clear the alert.\n- Invent a \"mitigation\" not stated in the advisory.\n\nInstead, collect these into a \"no fix available\" list during phase\n4 and surface them to the user verbatim in the report (phase 7):\nCVE / severity / module / pinned version / advisory URL. They are\ntracked, not patched.\n\n## Trigger fit\n\nStrong fit when the user says any of:\n\n- \"patch the CVEs\", \"fix the CVEs\", \"clear the OSV alerts\"\n- \"bump X for the CVE\", \"address GHSA-...\", \"what CVEs do we have?\"\n- \"scanner is complaining about Y\", \"osv-scanner findings\"\n- \"update deps for security\"\n\nSkip when the user wants a non-security dep bump (no CVE motivation) —\n`hack/bump_deps.sh` and a plain `go get` are usually right for those.\n\n## Tooling\n\nThis is a Go shop. The repo does not ship a Python toolchain and many\ncontributors do not have one installed — `pip install pyyaml` is **not**\na viable step.\n\nFor every parsing, filtering, or shaping task in this workflow, use:\n\n- **`jq`** for JSON. Required by `hack/osvtool` already, so present\n  whenever osvtool runs.\n- **`yq`** for YAML. Also required by `hack/osvtool`. Use\n  `yq -p=yaml -o=json` to convert the osvtool YAML back into JSON so\n  `jq` can do the heavy lifting.\n- **`go`**, `make`, standard shell builtins.\n\nDo **not** reach for `python`, `python3`, `node`, `ruby`, or\n`pip`/`npm install <anything>`. If you find yourself wanting one, write\na `jq` expression instead — every shape in the osvtool output (alerts,\nrule metadata, message text, help tables) is straightforward to handle\nin jq.\n\nConcrete starter: to round-trip the saved YAML back to a JSON array of\nalerts and start filtering:\n\n```bash\nyq -p=yaml -o=json /tmp/cve-bump/alerts-<branch>-<ts>.yaml \\\n  | jq '[ .[] | {\n      ghsa: .rule.id,\n      sev:  .rule.security_severity_level,\n      msg:  .most_recent_instance.message.text,\n      help: .rule.help,\n      url:  .html_url\n    } ]'\n```\n\nCVE IDs typically appear in `rule.full_description` or in the message\ntext alongside the package and version; the `help` field contains the\nGO-XXXX-XXXX advisory table with fixed versions. Extract with `jq`'s\n`capture` / regex helpers or a small `sed`/`grep` pipeline, not Python.\n\n## Workflow\n\nThere are six phases. Do them in order; do not skip the planning phase\neven if there is only one finding.\n\n### 1. Show the landscape (cross-branch summary)\n\nBefore pulling raw alerts, run the table mode across all supported\nbranches **for both targets** so the user sees source and image\nfindings side by side. With no `--branch` flags, `osvtool` scans\nexactly the branches allowlisted in the OSV scan workflow\n(`.github/workflows/osv-scanner.yaml`), so this stays correct as LTS\nbranches are cut or retired:\n\n```bash\n# Source-target landscape (the planning input for this skill)\n./hack/osvtool --table --target source --state open\n\n# Image-target landscape (will be revisited in phase 8)\n./hack/osvtool --table --target image --state open\n```\n\nEach call emits a compact markdown table — one row per branch,\ncolumns: `branch | C | H | M | L | max age | last scan |\ncode-scanning`. Show both to the user as-is; together they fit in\none screen and make image vs. source scope explicit.\n\nUse `--state open` (not `todo`) here so the medium/low counts are\nvisible. The user is about to decide whether to include them; they\nshould see the numbers first.\n\n**Do not** use the default YAML summary mode (without `--table`) for\nthis — it emits ~60 lines per branch of nested fields the user does\nnot need. If you ever want the full structured form (e.g. to grep a\nspecific field), pipe it to a file under `/tmp/cve-bump/` instead of\ninto the chat.\n\n### 2. Decide severity scope and target branch\n\nAsk the user two short questions, back to back:\n\n1. **Severity scope**: just critical/high (`--state todo`), or all open\n   severities including medium/low (`--state open`)? Recommend\n   critical/high since that matches how teams typically prioritize, but\n   the landscape view from phase 1 shows the full severity breakdown so\n   the user can make an informed call (e.g. if there are 30 mediums on\n   `v2.2.x` that they want to clear ahead of a release). Just ask in\n   one sentence — no menu.\n\n2. **Branch to plan against**: the raw pull is single-branch because\n   planning depends on that branch's go.mod state. Run the detection\n   helper for a default:\n\n   ```bash\n   .claude/skills/cve-bump/scripts/detect_base.sh\n   ```\n\n   It prints one line — the supported branch the current HEAD is most\n   likely based on (most-recent merge-base across the branches the OSV\n   scan workflow allowlists, read from\n   `.github/workflows/osv-scanner.yaml`), or `main` if it cannot\n   decide. Confirm with the user: \"Planning against `<X>`. OK, or pick\n   another?\"\n\nIf the landscape view in phase 1 showed zero alerts at the chosen\nseverity for the chosen branch, stop here and tell the user — there is\nnothing to plan.\n\n### 3. Pull the raw alerts\n\n```bash\n./hack/osvtool --raw --branch <branch> --target source --state <state>\n```\n\nWhere `<state>` is whatever the user picked in phase 2 (`todo` or\n`open`).\n\nNotes:\n\n- `--target source` filters this pass to Go-mod findings only.\n  Image findings are addressed separately in phase 8 because their\n  actions are different (Dockerfile / base-image / apk changes) and\n  many of them resolve transitively once the source bumps land.\n  Do not try to plan them in the same table.\n- The output is YAML. Save it to a scratch path outside the repo so it\n  is not accidentally committed. Use\n  `/tmp/cve-bump/alerts-<branch>-<UTC-timestamp>.yaml` (mkdir -p first).\n  This gives the user a record and lets us re-parse without another\n  GitHub call.\n\nIf `osvtool` errors with an auth message, surface the help text it\nprints — it already explains the fix (`gh auth refresh --scopes\nsecurity_events` or a fine-grained PAT). Do not paper over it.\n\n### 4. Plan one action per finding\n\nFor each alert, extract:\n\n- CVE ID (`rule.id` is typically the GHSA; the CVE lives in\n  `rule.full_description` or `most_recent_instance.message.text`).\n- Severity (`rule.security_severity_level`).\n- Affected Go module + current pinned version.\n- Fixed version (the lowest version that resolves the advisory).\n  May be **absent** — see the no-fix branch below.\n- Whether the module is direct or indirect in this repo's go.mod.\n- Whether the module is a [protected dependency](#protected-dependencies).\n\nThen pick an action per the decision tree below.\n\n#### Decision tree\n\n```mermaid\nflowchart TD\n    A[Finding] --> N{Is there a fixed version<br/>in the advisory?}\n    N -- no --> NF[Report only:<br/>add to \"no fix available\" list]\n    N -- yes --> P{Is the module a protected dep<br/>envoy / k8s.io/* / istio.io/* / gateway-api?}\n    P -- yes --> PA[Needs approval:<br/>row in plan with action \"needs approval protected dep\"<br/>do not include in batch go get]\n    P -- no --> B{Is the pinned version actually >= fix,<br/>once you account for pseudo-versions?}\n    B -- yes --> C[False positive → add to osv-scanner.toml]\n    B -- no --> D{Is the module direct in go.mod?}\n    D -- yes --> E[go get module@fixed-version]\n    D -- no --> F{Does a single parent dep<br/>own this transitive?}\n    F -- yes, and bumping it is reasonable --> G[go get parent@version-that-pulls-fix]\n    F -- no, or parent bump too risky --> H[Add explicit require<br/>go get module@fixed-version<br/>force MVS to pick the fix]\n```\n\n**Detecting pseudo-version false positives**: when the affected version\nlooks like `v0.0.0-<14-digit-timestamp>-<sha>` and the fix version is a\nnormal semver like `1.x`, OSV-Scanner compares them lexically as\n`\"0.0.0\" < \"1.x\"` and fires regardless of whether the pinned commit\nalready includes the fix. Read the timestamp embedded in the\npseudo-version and compare to the fix release date. If the pinned\ncommit is newer, it is a false positive — add to `osv-scanner.toml`\nwith a reason that names the timestamp, the fix release date, and the\nyears/months gap. The five istio.io entries already in that file are\nthe model to copy.\n\n**Choosing between parent bump and explicit require**: prefer the\nparent bump when (a) the parent is itself behind by only a minor/patch\nversion, and (b) its changelog does not flag breaking changes for our\nusage. Otherwise add an explicit require — it is the smallest possible\nintervention and Go's MVS will honor it.\n\n#### Output: the plan\n\nPresent the plan as up to three fenced tables so each bucket is\nvisually distinct.\n\n**1. Actionable bumps** — what you intend to `go get` or ignore.\nColumns: `CVE | severity | module | from → to | action`.\n\n```\n| CVE             | sev | module                | from           | to        | action                |\n| --------------- | --- | --------------------- | -------------- | --------- | --------------------- |\n| CVE-2025-1111   | H   | golang.org/x/net      | v0.20.0        | v0.27.0   | go get (direct)       |\n| CVE-2025-2222   | M   | gopkg.in/foo.v3       | v3.1.0         | v3.2.1    | explicit require      |\n| CVE-2022-31045  | H   | istio.io/istio        | v0.0.0-2026... | (n/a)     | ignore: false pos     |\n```\n\n**2. Needs approval — protected deps** (omit the table if empty).\nColumns: `CVE | severity | module | from → to | reason`. The user\nmust say yes per row before any of these move into the actionable\nbatch.\n\n```\n| CVE             | sev | module                  | from     | to       | reason                  |\n| --------------- | --- | ----------------------- | -------- | -------- | ----------------------- |\n| CVE-2025-9999   | H   | k8s.io/apimachinery     | v0.31.2  | v0.31.5  | only fix is a k8s.io bump |\n```\n\n**3. No fix available** (omit the table if empty). Columns:\n`CVE | severity | module | pinned | advisory`. Read-only — no\naction proposed. These will be repeated verbatim in the final\nreport.\n\n```\n| CVE             | sev | module                | pinned    | advisory                                        |\n| --------------- | --- | --------------------- | --------- | ----------------------------------------------- |\n| CVE-2025-7777   | M   | example.com/foo       | v1.4.0    | https://github.com/advisories/GHSA-xxxx-yyyy-zz |\n```\n\nThen ask the user to OK the actionable table, decide each\nprotected-dep row, and acknowledge the no-fix list. Wait for\nexplicit approval before touching files.\n\n### 5. Execute\n\nAfter approval, in order:\n\n1. **Protected-dep gate.** For any row whose action was `needs\n   approval (protected dep)`, only proceed if the user explicitly\n   said yes to that row. If unclear, ask again. Do not batch\n   protected deps into step 2 — `go get` them one at a time so the\n   user can stop the flow if a transitive surprise shows up.\n2. For each remaining `go get` action: `go get <module>@<version>`.\n3. `go mod tidy` once at the end.\n4. **Check for surprise protected-dep transitives.** After\n   `go mod tidy`, diff `go.mod` for any envoy / k8s.io / istio.io /\n   gateway-api lines that changed and were not in the approved\n   plan. If any did, stop, surface them to the user, and let them\n   decide (revert or approve).\n5. For each ignore action: append a `[[IgnoredVulns]]` entry to\n   `osv-scanner.toml`. The `reason` must be specific — name the\n   pseudo-version timestamp, the fix release date, and the gap. Do not\n   write generic reasons like \"false positive\" or \"not applicable\".\n   Never add an ignore entry for a no-fix CVE or a protected-dep\n   CVE just to clear noise.\n6. If `go.sum` changed, run `make generate-licenses` to refresh the\n   license attribution files.\n7. Run `make fmt-changed` to keep the diff clean.\n\nDo not stage anything (`git add`) and do not commit. The user reviews\nthe dirty tree themselves.\n\n### 6. Verify\n\nThe user signed off on \"build + scanner re-run only\", so:\n\n1. `go build -tags e2e ./...` — the `e2e` tag is required to compile\n   all source per the repo's CLAUDE.md. If the build fails, the bump is\n   not safe; stop and report.\n2. Local scanner re-run: **`make osv-scan`**. This is the canonical\n   way to run OSV-Scanner locally in kgateway — it shells out to\n   Docker (image `ghcr.io/google/osv-scanner-action`), so you do not\n   need a local `osv-scanner` binary. Do **not** reach for a\n   standalone `osv-scanner` invocation; the make target handles config\n   lookup, output paths, and reporter conversion in one shot.\n   - Results land at `_output/osv/<safe-branch>/results.json` (and a\n     SARIF sibling). `<safe-branch>` is the current branch with `/` and\n     `.` replaced by `-`.\n   - The `make osv-scan` JSON schema is osv-scanner native\n     (`results[].packages[].vulnerabilities[].id`), which is different\n     from the GitHub code-scanning YAML from phase 3. To diff, extract\n     the set of vuln IDs from each side with `jq`/`yq` and compare the\n     sets — don't try to align records field-by-field. Report which\n     CVEs the user expected to clear that still appear, and any new\n     ones the scanner surfaced that were not in the original plan.\n3. If Docker is not running, `make osv-scan` will fail with a Docker\n   error. Tell the user and stop — do not try to install osv-scanner\n   from source as a workaround.\n\n### 7. Report\n\nEnd with the following sections, in order:\n\n1. **What changed.** One line per CVE that got an action: CVE,\n   module, from → to (or \"ignore: <reason summary>\"), and the\n   file(s) touched. The user reads this against `git diff`.\n2. **Deferred — protected dep.** Any protected-dep rows the user\n   said no to (or did not respond to). List CVE / module / proposed\n   version / why deferred so the next maintainer has the context.\n3. **No fix available.** Verbatim from the phase-4 table — CVE /\n   severity / module / pinned / advisory URL. Do not propose\n   workarounds. Do not add `osv-scanner.toml` entries for these.\n\nThen continue into phase 8.\n\n### 8. Image-target follow-up\n\nImage findings often resolve transitively once source bumps land\n(e.g. a Go binary baked into the image gets the fixed dep), but\nsome do not — base-image CVEs and apk packages need Dockerfile or\nbase-image changes.\n\nRefresh the image landscape against the planned branch:\n\n```bash\n./hack/osvtool --table \\\n    --branch <branch> \\\n    --target image --state open\n```\n\nThen prompt the user: \"The source bumps are done. Image findings\nabove remain on the published scan — most should clear once these\nbumps ship, but base-image / apk CVEs need a Dockerfile change.\nWant to plan that now, or defer?\"\n\nTwo important caveats to state plainly:\n\n- The `--table` view reflects the **last GitHub-uploaded scan**,\n  not the working tree. The source bumps you just made won't be\n  reflected in image scans until CI rebuilds and republishes\n  results.\n- A local image scan is possible via\n  `make osv-scan OSV_SCAN_IMAGES=\"<image-ref> ...\"` (see\n  `osv-scan-latest-main-images` in the Makefile for the canonical\n  invocation against rolling-main images), but that scans\n  **already-built published images**, not the just-bumped working\n  tree. Mention this if the user asks why a \"fixed\" CVE still\n  appears locally.\n\nIf the user says \"defer\", stop here. If they say \"go\", treat the\nimage flow as a separate planning round — start a new plan with\nDockerfile-level actions (do not extend the Go-mod plan with image\nactions, they live in different files and the decision tree above\ndoes not apply).\n\n## Edge cases worth knowing\n\n- **Pseudo-version on the fix side**: rare, but if a fix only exists at\n  a non-tagged commit, `go get module@<sha>` works. Mark this clearly\n  in the plan so the user is not surprised by a pseudo-version in\n  go.mod.\n- **Module renamed/moved**: if the advisory's module path no longer\n  exists upstream, a `replace` directive may be necessary. Treat this\n  as ambiguous and flag it for the user instead of writing the replace\n  silently.\n- **Multiple CVEs on one module**: combine into a single `go get` to\n  the highest required version. List them as separate rows in the plan\n  table but mark \"(combined bump)\" in the action column.\n- **Backport branches**: when scanning `v2.2.x` etc., the supported\n  upgrade window for a given module may be narrower (e.g. cannot move\n  to a new major). Default to the smallest version that resolves the\n  CVE within the same major.\n- **Topic branch with no alerts on the base branch**: possible if the\n  branch was just rebased after a recent scan. Tell the user; offer to\n  re-run after CI uploads fresh results.\n\n## Files this skill touches\n\n- `go.mod` / `go.sum` — version bumps\n- `osv-scanner.toml` — false-positive ignore entries (root-level; this\n  is the only scanner ignore file in this repo as of writing — `.snyk`,\n  `.grype.yaml`, `.trivyignore` are not present)\n- License attribution files, if `make generate-licenses` is wired up\n\n## Bundled scripts\n\n- `scripts/detect_base.sh` — prints the supported branch that the\n  current HEAD is most likely based on. Used in phase 1.\n\n## Things not to do\n\n- Do **not** commit or stage. The user does that.\n- Do **not** edit go.mod by hand — always go through `go get` so\n  go.sum stays consistent.\n- Do **not** bump a [protected dependency](#protected-dependencies)\n  without an explicit, in-conversation yes from the user — not in a\n  batch, not as a transitive pulled in by a parent bump. Surface it\n  for approval and wait.\n- Do **not** invent a workaround for a CVE with no upstream fix.\n  Do not pin to a fork, do not silence it via `osv-scanner.toml`.\n  List it in the no-fix bucket and move on.\n- Do **not** stop after the source bumps — phase 8 (image\n  follow-up) is part of the workflow. Prompt the user about\n  remaining image findings every time.\n- Do **not** add an `osv-scanner.toml` entry just to silence noise.\n  The reason field has to defend the entry to a future reader.\n- Do **not** run `make generate-all` or other heavy codegen targets\n  for a dep bump — they are not needed for non-API changes and they\n  slow the loop down by ~30s for no benefit.\n- Do **not** scan an unsupported branch (e.g. `chandler/cveskill`)\n  directly via `osvtool` — there are no GitHub-uploaded alerts for it.\n  (Note: `make osv-scan` does work on any branch because it runs the\n  scanner locally; that is fine for verification.)\n- Do **not** reach for Python, Node, or any other parser. Use `jq`\n  and `yq` — see the Tooling section.\n- Do **not** invoke `osv-scanner` directly. Use `make osv-scan` — it\n  runs the scanner in Docker with the right config and output paths.\n- Do **not** dump the default YAML summary into the chat for the\n  cross-branch landscape view. Use `./hack/osvtool --table` (phase 1)\n  — it's a single small markdown table instead of dozens of lines of\n  nested YAML.\n","frontmatter":{"name":"cve-bump","description":"Patch CVEs surfaced by the kgateway OSV scanner by bumping the affected Go module dependencies (direct, indirect via require, or replace) and curating false-positive entries in osv-scanner.toml. Use this skill whenever the user wants to fix CVEs, address OSV-Scanner alerts, bump deps for security reasons, triage code-scanning findings, \"see what CVEs we have\", or do anything CVE/security-bump-flavored in this repo — even if they don't name the skill explicitly."},"isInternal":false,"tokens":5658,"sizeBytes":21954},{"name":"README.md","path":"pkg/kgateway/extensions2/plugins/waypoint/README.md","rawUrl":"https://raw.githubusercontent.com/kgateway-dev/kgateway/HEAD/pkg/kgateway/extensions2/plugins/waypoint/README.md","title":"waypoint Documentation","category":"plugin-manifest","format":"markdown","content":"# Kgateway Istio Waypoint Integration\n\n**Note:** This document serves as a reference for some of key design decisions related to Kgateway Istio Waypoint integration. Rather than creating extensive documentation for each decision, this file collects important decision points to provide a centralized reference.\n\n## Authorization Policy Namespace Configuration\n\n### Overview\nThis directory contains the implementation for integrating Kgateway with Istio Waypoints, including support for waypoint authorization policies through GatewayClass as a TargetRef.\n\n### Root Namespace for Authorization Policies\nWhen implementing GatewayClass as a TargetRef for waypoint authorization policies, we needed to determine which namespace(s) should be used for global policies. After discussion among maintainers, the following decisions were made:\n\n1. **Default Root Namespace**: The default root namespace for waypoint authorization policies will be `istio-system` to maintain consistency with Istio's implementation.\n\n2. **Configurability**: The root namespace is configurable through Settings, allowing users to change it if needed.\n\n3. **Setting Name**: The configuration setting will be called \"Mesh root namespace\" to clearly indicate its purpose.\n\n### Rationale\n- Maintaining consistency with Istio's implementation reduces confusion for users, especially during migration.\n- The `istio-system` namespace is typically the permanent root namespace for mesh configurations.\n- Waypoint configurations should be consistent with ztunnel regardless of whether using Istio or kgateway.\n- Having a single configurable namespace (rather than multiple namespaces) avoids potential conflicts between duplicate authorization policies.\n\n### Usage\nAuthorization policies targeting waypoints should be placed in the `istio-system` namespace by default, or in the configured mesh root namespace if changed via Settings.\n\n```yaml\n# Example authorization policy in istio-system namespace\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: waypoint-policy\n  namespace: istio-system\nspec:\n  targetRef:\n    kind: GatewayClass\n    name: kgateway-waypoint\n  # Policy details...\n```\n\nFor users migrating from Istio to KGateway, using the default `istio-system` namespace simplifies the transition by allowing existing authorization policies to work with KGateway waypoints.","isInternal":false,"tokens":439,"sizeBytes":2373},{"name":"README.md","path":"tools/build-tools/README.md","rawUrl":"https://raw.githubusercontent.com/kgateway-dev/kgateway/HEAD/tools/build-tools/README.md","title":"build-tools Documentation","category":"plugin-manifest","format":"markdown","content":"## build-tools (Codespaces/devcontainer image)\n\nThis directory contains the Docker image definition for the `build-tools` devcontainer used by this repo.\n\nIt is **inspired by Istio's `build-tools` image** (from `istio/common-files`) and is intended to be\npublished to GitHub Container Registry (GHCR) so GitHub Codespaces can pull it quickly and reliably.\n\n### What’s included (high level)\n\n- Go (version matches `go.mod`)\n- Rust toolchain (for `internal/envoy_modules/`)\n- Common build tooling: `git`, `make`, `gcc`, `jq`, `yq`, `kubectl`, `kind`, `helm`, `protoc`, `buf`\n- Docker CLI (for `docker-outside-of-docker` feature)\n- `vim` (for editing) \n\n### Building locally\n\nYou can build the image locally with the make target:\n```bash\nmake build-tools-image\n```\n\nTest the build container locally:\n```shell\ndocker run -it -v \"$(pwd):/workspace\" -w /workspace kgateway-build-tools:dev\n```\n\nYou should be able to run kgateway commands (ie. `make generate-all`, `make run`, etc.) from within the container.\n\n### Testing as GitHub Codespaces would build it\n\nGitHub Codespaces uses the `@devcontainers/cli` to build and run the devcontainer. To reproduce\nexactly what Codespaces does (Dockerfile + devcontainer features like `docker-outside-of-docker`\nand `kind`), use the `devcontainer` CLI:\n\n```bash\n# Install the CLI (one-time)\nnpm install -g @devcontainers/cli\n\n# Build the image from the repo root (this is what Codespaces runs)\ndevcontainer build --workspace-folder .\n```\n\nThis builds the Dockerfile **and** installs the devcontainer features defined in\n`.devcontainer/devcontainer.json`, producing the same image a Codespace would use.\n\nTo run an interactive shell in the built image:\n```bash\ndevcontainer up --workspace-folder .\ndevcontainer exec --workspace-folder . bash\n```\n\n> **Note:** `make build-tools-image` only builds the Dockerfile and does **not** apply\n> devcontainer features. Use `devcontainer build` when you need to verify that features\n> (Docker CLI version pinning, kind, etc.) are configured correctly.\n\n### Using the devcontainer with VS Code\n\n1. Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.\n2. Open the root of this repo in VS Code.\n3. Click the \"Reopen in Container\" button.\n\nYou should now be able run kgateway commands inside the devcontainer instead of having to install dependencies locally.\n","isInternal":false,"tokens":578,"sizeBytes":2410}],"systemPromptSnippet":"<yakaai_skills repo=\"kgateway-dev/kgateway\">\n<!-- File: AGENTS.md (Tokens: ~1378 | Category: root-instruction) -->\n# Project Overview\nkgateway is a control plane implementing the Kubernetes Gateway API for Envoy. It's built on KRT (Kubernetes Declarative Controller Runtime from Istio) and uses a plugin-based architecture for extensibility.\n\n## Architecture\n\n### Translation Pipeline\nTranslation runs in 3 phases: Policy → IR, then HTTPRoute/Gateway → IR with policies attached, then IR → xDS. See `/devel/architecture/overview.md` and the translation diagram at `/devel/architecture/translation.svg`.\n\n### Plugin System\nkgateway translates Kubernetes Gateway API resources into Envoy configuration. Plugins *contribute* to that translation, usually by adding a new CRD (most commonly a Policy CRD) that users create to express their desired configuration. Policy CRDs attach to Gateway API resources via `targetRefs` or `targetSelectors`; kgateway manages the attachment during translation.\n\nConvert the CRD to an intermediate representation (IR) that is as close to Envoy protos as possible. This minimizes logic in the final translation and allows better status to be reported back to the user on errors.\n\nPlugins are **stateless across translations** but maintain state during a single gateway translation via `ProxyTranslationPass`. Each plugin provides a KRT collection of `ir.PolicyWrapper`, implements `NewGatewayTranslationPass`, and may hook backend processing — see `pkg/pluginsdk` for the interfaces.\n\nExample: `/pkg/kgateway/extensions2/plugins/trafficpolicy/traffic_policy_plugin.go`\n\n## Critical Developer Patterns\n\n### go build tag e2e\nIf you intend to include all source code, run `go` commands that accept `-tags` with `-tags e2e`.\n\n### IR Equals() Methods (STRICTLY ENFORCED)\nIRs output by KRT collections **must** implement `Equals(other T) bool`:\n- **Compare ALL fields** or mark with `// +noKrtEquals` (last line of comment)\n- `+krtEqualsTODO` exists only to track legacy gaps — never use it in new code\n- **Never use `reflect.DeepEqual`** — flagged by the custom `krtequals` analyzer (external module wired up in `.custom-gcl.yml`, configured in `.golangci.yaml`)\n- Use proto equality helpers: `proto.Equal()`, not `==`\n- Unit test the `Equals` method\n\nHigh-risk area: an incomplete `Equals` silently breaks KRT change detection.\n\n### Code Generation Workflow\n`make help` lists the codegen targets. `make generate-all` is the usual choice (stamp-based, only regenerates what changed); `make generated-code` force-regenerates everything. Always run `make fmt` or `make fmt-changed` before committing.\n\nAfter API changes: run `make go-generate-apis` then `make fmt-changed`. Dependency tracking lives in `_output/stamps/`; run `make clean-stamps` if regeneration seems stuck. If not sure, just run `make generate-all`.\n\n### Testing Conventions\n- **Unit tests**: For new code, avoid Ginkgo. You may use Gomega matchers if appropriate.\n- **E2E tests**: Use framework in `/test/e2e/` - DO NOT directly kubectl apply in tests\n- **Custom matchers**: `/test/gomega/matchers/` (e.g., `HaveHttpResponse`)\n- **Transforms**: Compose matchers with `WithTransform()` (see `/devel/testing/writing-tests.md`)\n- Prefer explicit error checking: `Expect(err).To(MatchError(\"msg\"))` over `HaveOccurred()`\n- Add descriptions: `Expect(x).To(BeEmpty(), \"list should be empty on init\")`\n- Never manually delete e2e resources in a specific order - let the framework handle teardown\n\n```bash\nmake test TEST_PKG=./path/to/package        # Unit tests\nmake e2e-test TEST_PKG=./test/e2e/tests/... # E2E tests\nmake unit                                   # All unit tests (excludes e2e)\n```\n\n### API/CRD Development\n\nRead the relevant doc BEFORE starting either of these — both have non-obvious required steps:\n- **Adding a new CRD**: `/api/README.md` (field conventions, codegen, and the client + fake-client registration steps).\n- **Adding a field to an existing Policy CRD**: `/devel/contributing/adding-policy-fields.md` (IR translation, golden-file refresh via `REFRESH_GOLDEN=true`, E2E registration).\n\nKey constraint either way: translate as close to the Envoy protos as possible in the IR, not in the translation pass — the translation pass should stay very lightweight.\n\n## Local Development\n```bash\nmake run                    # kind + CRDs + MetalLB + images + charts\nmake kind-reload-kgateway   # Rebuild, load, and restart after a code change\n\nmake conformance                               # Gateway API conformance\nmake all-conformance                           # All suites\nmake conformance-HTTPRouteSimpleSameNamespace  # Specific test by ShortName\n```\n\nPinned versions live in the `Makefile` (`ENVOY_IMAGE`, `ALPINE_BASE_IMAGE`, and `CONFORMANCE_VERSION` for the Gateway API CRDs); bump Gateway API with `make bump-gtw DEP_REF=v1.3.0`. Run `make help` for all targets.\n\n## Opening Pull Requests\n\n### Before you open\n1. `make verify` - regenerates code and fails if it produces a local diff\n2. `make analyze` - runs the linter\n3. `make lint-actions` - only if you modified files in `.github/`\n4. Every commit needs a `Signed-off-by` trailer ([DCO](https://developercertificate.org/) is a required check). Use `git commit -s`.\n\n### PR body\nPRs must follow `/.github/PULL_REQUEST_TEMPLATE.md`, which GitHub pre-fills in the web UI. **`gh pr create --body`/`--body-file` bypasses the template**, so when opening a PR from the CLI you must reproduce the structure yourself. The `labeler` workflow parses the body and is a **required check** — a missing change type or changelog block fails CI.\n\nFull details: `/devel/contributing/pull-requests.md` and `/.github/workflows/README.md`.\n\n## Further Reading\nDocs not already linked above: `/devel/contributing/README.md` (contributing overview), `/devel/contributing/conventions.md` (coding conventions), `/devel/contributing/code-generation.md` (codegen internals).\n\n<!-- File: CLAUDE.md (Tokens: ~3 | Category: claude-rule) -->\nAGENTS.md\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~96 | Category: copilot-instructions) -->\n# Go Version\nThis project uses **Go 1.26**.\n\n# Syntax Overrides\nThe following syntax is valid in Go 1.26 and must NOT be flagged as an error:\n- `new(value)`: The `new()` function now accepts expressions/values, not just types.\n  - VALID: `new(\"string_literal\")`, `new(123)`, `new(true)`\n  - DO NOT suggest replacing these with helper functions like `ptr.To()`.\n\n<!-- File: .claude/skills/cve-bump/SKILL.md (Tokens: ~5658 | Category: anthropic-skill) -->\n---\nname: cve-bump\ndescription: Patch CVEs surfaced by the kgateway OSV scanner by bumping the affected Go module dependencies (direct, indirect via require, or replace) and curating false-positive entries in osv-scanner.toml. Use this skill whenever the user wants to fix CVEs, address OSV-Scanner alerts, bump deps for security reasons, triage code-scanning findings, \"see what CVEs we have\", or do anything CVE/security-bump-flavored in this repo — even if they don't name the skill explicitly.\n---\n\n# cve-bump\n\nEnd-to-end workflow for clearing CVEs in kgateway's Go dependencies. The user\nwants a single pass: discover the alerts → decide the action for each →\npresent a plan → apply the bumps and ignore-list updates → verify locally.\n\n## Why this skill exists\n\nCVE patching in kgateway is more mechanical than it looks, but the moving\nparts are easy to miss:\n\n- The scanner findings live in GitHub code-scanning, not the local tree.\n  `hack/osvtool` is the canonical way to fetch them.\n- Findings are uploaded per-branch and only the branches allowlisted in\n  the OSV scan workflow (`.github/workflows/osv-scanner.yaml`) get\n  scanned, so a topic branch has to query its base branch.\n- Some \"vulnerabilities\" are scanner false positives caused by\n  pseudo-version comparison quirks (see the istio.io entries in\n  `osv-scanner.toml`). These belong in the ignore list, not in go.mod\n  churn.\n- Indirect deps need either a `go get` of the parent that pulls in the\n  fixed version, or an explicit `require` to force MVS to pick the fixed\n  version. Getting this wrong looks like the bump \"didn't take.\"\n\nThe skill encodes those decisions so the human only has to approve the\nplan, not relearn the rules each time.\n\n## Protected dependencies\n\nSome dependencies are load-bearing enough that bumping them risks\nbreaking the dataplane, compatibility with managed Kubernetes\nversions, or the Gateway API conformance contract. **Never bump any\nof these without an explicit, in-conversation yes from the user —\neven for a critical CVE.**\n\n- `github.com/envoyproxy/*` (Envoy and `go-control-plane`)\n- `k8s.io/*` (api, apimachinery, client-go, ...)\n- `istio.io/*`\n- `sigs.k8s.io/gateway-api` and other Gateway API modules\n\nHow this gets enforced through the workflow:\n\n- In the plan (phase 4), surface protected-dep rows with the action\n  `needs approval (protected dep)` and list them in their own\n  bucket. Do not fold them into a batch `go get`.\n- In the execute step (phase 5), confirm each protected-dep bump\n  one-by-one before running `go get`. If the user says no, leave\n  the dep alone and list the CVE under \"deferred — protected dep\"\n  in the report.\n- Never silence a protected-dep CVE via `osv-scanner.toml` to avoid\n  asking. The ignore list is for false positives only.\n\nIf a transitive bump (parent dep) would pull a new version of a\nprotected dep through MVS, that still counts as bumping the\nprotected dep. Detect it (`go list -m -json <module>` after the\nparent bump) and bring it back to the user.\n\n## CVEs with no known fix\n\nOSV-Scanner sometimes surfaces advisories that have no fixed\nversion yet (or were withdrawn upstream). Do not:\n\n- Pin to a fork or a personal patch.\n- Add an `osv-scanner.toml` ignore entry just to clear the alert.\n- Invent a \"mitigation\" not stated in the advisory.\n\nInstead, collect these into a \"no fix available\" list during phase\n4 and surface them to the user verbatim in the report (phase 7):\nCVE / severity / module / pinned version / advisory URL. They are\ntracked, not patched.\n\n## Trigger fit\n\nStrong fit when the user says any of:\n\n- \"patch the CVEs\", \"fix the CVEs\", \"clear the OSV alerts\"\n- \"bump X for the CVE\", \"address GHSA-...\", \"what CVEs do we have?\"\n- \"scanner is complaining about Y\", \"osv-scanner findings\"\n- \"update deps for security\"\n\nSkip when the user wants a non-security dep bump (no CVE motivation) —\n`hack/bump_deps.sh` and a plain `go get` are usually right for those.\n\n## Tooling\n\nThis is a Go shop. The repo does not ship a Python toolchain and many\ncontributors do not have one installed — `pip install pyyaml` is **not**\na viable step.\n\nFor every parsing, filtering, or shaping task in this workflow, use:\n\n- **`jq`** for JSON. Required by `hack/osvtool` already, so present\n  whenever osvtool runs.\n- **`yq`** for YAML. Also required by `hack/osvtool`. Use\n  `yq -p=yaml -o=json` to convert the osvtool YAML back into JSON so\n  `jq` can do the heavy lifting.\n- **`go`**, `make`, standard shell builtins.\n\nDo **not** reach for `python`, `python3`, `node`, `ruby`, or\n`pip`/`npm install <anything>`. If you find yourself wanting one, write\na `jq` expression instead — every shape in the osvtool output (alerts,\nrule metadata, message text, help tables) is straightforward to handle\nin jq.\n\nConcrete starter: to round-trip the saved YAML back to a JSON array of\nalerts and start filtering:\n\n```bash\nyq -p=yaml -o=json /tmp/cve-bump/alerts-<branch>-<ts>.yaml \\\n  | jq '[ .[] | {\n      ghsa: .rule.id,\n      sev:  .rule.security_severity_level,\n      msg:  .most_recent_instance.message.text,\n      help: .rule.help,\n      url:  .html_url\n    } ]'\n```\n\nCVE IDs typically appear in `rule.full_description` or in the message\ntext alongside the package and version; the `help` field contains the\nGO-XXXX-XXXX advisory table with fixed versions. Extract with `jq`'s\n`capture` / regex helpers or a small `sed`/`grep` pipeline, not Python.\n\n## Workflow\n\nThere are six phases. Do them in order; do not skip the planning phase\neven if there is only one finding.\n\n### 1. Show the landscape (cross-branch summary)\n\nBefore pulling raw alerts, run the table mode across all supported\nbranches **for both targets** so the user sees source and image\nfindings side by side. With no `--branch` flags, `osvtool` scans\nexactly the branches allowlisted in the OSV scan workflow\n(`.github/workflows/osv-scanner.yaml`), so this stays correct as LTS\nbranches are cut or retired:\n\n```bash\n# Source-target landscape (the planning input for this skill)\n./hack/osvtool --table --target source --state open\n\n# Image-target landscape (will be revisited in phase 8)\n./hack/osvtool --table --target image --state open\n```\n\nEach call emits a compact markdown table — one row per branch,\ncolumns: `branch | C | H | M | L | max age | last scan |\ncode-scanning`. Show both to the user as-is; together they fit in\none screen and make image vs. source scope explicit.\n\nUse `--state open` (not `todo`) here so the medium/low counts are\nvisible. The user is about to decide whether to include them; they\nshould see the numbers first.\n\n**Do not** use the default YAML summary mode (without `--table`) for\nthis — it emits ~60 lines per branch of nested fields the user does\nnot need. If you ever want the full structured form (e.g. to grep a\nspecific field), pipe it to a file under `/tmp/cve-bump/` instead of\ninto the chat.\n\n### 2. Decide severity scope and target branch\n\nAsk the user two short questions, back to back:\n\n1. **Severity scope**: just critical/high (`--state todo`), or all open\n   severities including medium/low (`--state open`)? Recommend\n   critical/high since that matches how teams typically prioritize, but\n   the landscape view from phase 1 shows the full severity breakdown so\n   the user can make an informed call (e.g. if there are 30 mediums on\n   `v2.2.x` that they want to clear ahead of a release). Just ask in\n   one sentence — no menu.\n\n2. **Branch to plan against**: the raw pull is single-branch because\n   planning depends on that branch's go.mod state. Run the detection\n   helper for a default:\n\n   ```bash\n   .claude/skills/cve-bump/scripts/detect_base.sh\n   ```\n\n   It prints one line — the supported branch the current HEAD is most\n   likely based on (most-recent merge-base across the branches the OSV\n   scan workflow allowlists, read from\n   `.github/workflows/osv-scanner.yaml`), or `main` if it cannot\n   decide. Confirm with the user: \"Planning against `<X>`. OK, or pick\n   another?\"\n\nIf the landscape view in phase 1 showed zero alerts at the chosen\nseverity for the chosen branch, stop here and tell the user — there is\nnothing to plan.\n\n### 3. Pull the raw alerts\n\n```bash\n./hack/osvtool --raw --branch <branch> --target source --state <state>\n```\n\nWhere `<state>` is whatever the user picked in phase 2 (`todo` or\n`open`).\n\nNotes:\n\n- `--target source` filters this pass to Go-mod findings only.\n  Image findings are addressed separately in phase 8 because their\n  actions are different (Dockerfile / base-image / apk changes) and\n  many of them resolve transitively once the source bumps land.\n  Do not try to plan them in the same table.\n- The output is YAML. Save it to a scratch path outside the repo so it\n  is not accidentally committed. Use\n  `/tmp/cve-bump/alerts-<branch>-<UTC-timestamp>.yaml` (mkdir -p first).\n  This gives the user a record and lets us re-parse without another\n  GitHub call.\n\nIf `osvtool` errors with an auth message, surface the help text it\nprints — it already explains the fix (`gh auth refresh --scopes\nsecurity_events` or a fine-grained PAT). Do not paper over it.\n\n### 4. Plan one action per finding\n\nFor each alert, extract:\n\n- CVE ID (`rule.id` is typically the GHSA; the CVE lives in\n  `rule.full_description` or `most_recent_instance.message.text`).\n- Severity (`rule.security_severity_level`).\n- Affected Go module + current pinned version.\n- Fixed version (the lowest version that resolves the advisory).\n  May be **absent** — see the no-fix branch below.\n- Whether the module is direct or indirect in this repo's go.mod.\n- Whether the module is a [protected dependency](#protected-dependencies).\n\nThen pick an action per the decision tree below.\n\n#### Decision tree\n\n```mermaid\nflowchart TD\n    A[Finding] --> N{Is there a fixed version<br/>in the advisory?}\n    N -- no --> NF[Report only:<br/>add to \"no fix available\" list]\n    N -- yes --> P{Is the module a protected dep<br/>envoy / k8s.io/* / istio.io/* / gateway-api?}\n    P -- yes --> PA[Needs approval:<br/>row in plan with action \"needs approval protected dep\"<br/>do not include in batch go get]\n    P -- no --> B{Is the pinned version actually >= fix,<br/>once you account for pseudo-versions?}\n    B -- yes --> C[False positive → add to osv-scanner.toml]\n    B -- no --> D{Is the module direct in go.mod?}\n    D -- yes --> E[go get module@fixed-version]\n    D -- no --> F{Does a single parent dep<br/>own this transitive?}\n    F -- yes, and bumping it is reasonable --> G[go get parent@version-that-pulls-fix]\n    F -- no, or parent bump too risky --> H[Add explicit require<br/>go get module@fixed-version<br/>force MVS to pick the fix]\n```\n\n**Detecting pseudo-version false positives**: when the affected version\nlooks like `v0.0.0-<14-digit-timestamp>-<sha>` and the fix version is a\nnormal semver like `1.x`, OSV-Scanner compares them lexically as\n`\"0.0.0\" < \"1.x\"` and fires regardless of whether the pinned commit\nalready includes the fix. Read the timestamp embedded in the\npseudo-version and compare to the fix release date. If the pinned\ncommit is newer, it is a false positive — add to `osv-scanner.toml`\nwith a reason that names the timestamp, the fix release date, and the\nyears/months gap. The five istio.io entries already in that file are\nthe model to copy.\n\n**Choosing between parent bump and explicit require**: prefer the\nparent bump when (a) the parent is itself behind by only a minor/patch\nversion, and (b) its changelog does not flag breaking changes for our\nusage. Otherwise add an explicit require — it is the smallest possible\nintervention and Go's MVS will honor it.\n\n#### Output: the plan\n\nPresent the plan as up to three fenced tables so each bucket is\nvisually distinct.\n\n**1. Actionable bumps** — what you intend to `go get` or ignore.\nColumns: `CVE | severity | module | from → to | action`.\n\n```\n| CVE             | sev | module                | from           | to        | action                |\n| --------------- | --- | --------------------- | -------------- | --------- | --------------------- |\n| CVE-2025-1111   | H   | golang.org/x/net      | v0.20.0        | v0.27.0   | go get (direct)       |\n| CVE-2025-2222   | M   | gopkg.in/foo.v3       | v3.1.0         | v3.2.1    | explicit require      |\n| CVE-2022-31045  | H   | istio.io/istio        | v0.0.0-2026... | (n/a)     | ignore: false pos     |\n```\n\n**2. Needs approval — protected deps** (omit the table if empty).\nColumns: `CVE | severity | module | from → to | reason`. The user\nmust say yes per row before any of these move into the actionable\nbatch.\n\n```\n| CVE             | sev | module                  | from     | to       | reason                  |\n| --------------- | --- | ----------------------- | -------- | -------- | ----------------------- |\n| CVE-2025-9999   | H   | k8s.io/apimachinery     | v0.31.2  | v0.31.5  | only fix is a k8s.io bump |\n```\n\n**3. No fix available** (omit the table if empty). Columns:\n`CVE | severity | module | pinned | advisory`. Read-only — no\naction proposed. These will be repeated verbatim in the final\nreport.\n\n```\n| CVE             | sev | module                | pinned    | advisory                                        |\n| --------------- | --- | --------------------- | --------- | ----------------------------------------------- |\n| CVE-2025-7777   | M   | example.com/foo       | v1.4.0    | https://github.com/advisories/GHSA-xxxx-yyyy-zz |\n```\n\nThen ask the user to OK the actionable table, decide each\nprotected-dep row, and acknowledge the no-fix list. Wait for\nexplicit approval before touching files.\n\n### 5. Execute\n\nAfter approval, in order:\n\n1. **Protected-dep gate.** For any row whose action was `needs\n   approval (protected dep)`, only proceed if the user explicitly\n   said yes to that row. If unclear, ask again. Do not batch\n   protected deps into step 2 — `go get` them one at a time so the\n   user can stop the flow if a transitive surprise shows up.\n2. For each remaining `go get` action: `go get <module>@<version>`.\n3. `go mod tidy` once at the end.\n4. **Check for surprise protected-dep transitives.** After\n   `go mod tidy`, diff `go.mod` for any envoy / k8s.io / istio.io /\n   gateway-api lines that changed and were not in the approved\n   plan. If any did, stop, surface them to the user, and let them\n   decide (revert or approve).\n5. For each ignore action: append a `[[IgnoredVulns]]` entry to\n   `osv-scanner.toml`. The `reason` must be specific — name the\n   pseudo-version timestamp, the fix release date, and the gap. Do not\n   write generic reasons like \"false positive\" or \"not applicable\".\n   Never add an ignore entry for a no-fix CVE or a protected-dep\n   CVE just to clear noise.\n6. If `go.sum` changed, run `make generate-licenses` to refresh the\n   license attribution files.\n7. Run `make fmt-changed` to keep the diff clean.\n\nDo not stage anything (`git add`) and do not commit. The user reviews\nthe dirty tree themselves.\n\n### 6. Verify\n\nThe user signed off on \"build + scanner re-run only\", so:\n\n1. `go build -tags e2e ./...` — the `e2e` tag is required to compile\n   all source per the repo's CLAUDE.md. If the build fails, the bump is\n   not safe; stop and report.\n2. Local scanner re-run: **`make osv-scan`**. This is the canonical\n   way to run OSV-Scanner locally in kgateway — it shells out to\n   Docker (image `ghcr.io/google/osv-scanner-action`), so you do not\n   need a local `osv-scanner` binary. Do **not** reach for a\n   standalone `osv-scanner` invocation; the make target handles config\n   lookup, output paths, and reporter conversion in one shot.\n   - Results land at `_output/osv/<safe-branch>/results.json` (and a\n     SARIF sibling). `<safe-branch>` is the current branch with `/` and\n     `.` replaced by `-`.\n   - The `make osv-scan` JSON schema is osv-scanner native\n     (`results[].packages[].vulnerabilities[].id`), which is different\n     from the GitHub code-scanning YAML from phase 3. To diff, extract\n     the set of vuln IDs from each side with `jq`/`yq` and compare the\n     sets — don't try to align records field-by-field. Report which\n     CVEs the user expected to clear that still appear, and any new\n     ones the scanner surfaced that were not in the original plan.\n3. If Docker is not running, `make osv-scan` will fail with a Docker\n   error. Tell the user and stop — do not try to install osv-scanner\n   from source as a workaround.\n\n### 7. Report\n\nEnd with the following sections, in order:\n\n1. **What changed.** One line per CVE that got an action: CVE,\n   module, from → to (or \"ignore: <reason summary>\"), and the\n   file(s) touched. The user reads this against `git diff`.\n2. **Deferred — protected dep.** Any protected-dep rows the user\n   said no to (or did not respond to). List CVE / module / proposed\n   version / why deferred so the next maintainer has the context.\n3. **No fix available.** Verbatim from the phase-4 table — CVE /\n   severity / module / pinned / advisory URL. Do not propose\n   workarounds. Do not add `osv-scanner.toml` entries for these.\n\nThen continue into phase 8.\n\n### 8. Image-target follow-up\n\nImage findings often resolve transitively once source bumps land\n(e.g. a Go binary baked into the image gets the fixed dep), but\nsome do not — base-image CVEs and apk packages need Dockerfile or\nbase-image changes.\n\nRefresh the image landscape against the planned branch:\n\n```bash\n./hack/osvtool --table \\\n    --branch <branch> \\\n    --target image --state open\n```\n\nThen prompt the user: \"The source bumps are done. Image findings\nabove remain on the published scan — most should clear once these\nbumps ship, but base-image / apk CVEs need a Dockerfile change.\nWant to plan that now, or defer?\"\n\nTwo important caveats to state plainly:\n\n- The `--table` view reflects the **last GitHub-uploaded scan**,\n  not the working tree. The source bumps you just made won't be\n  reflected in image scans until CI rebuilds and republishes\n  results.\n- A local image scan is possible via\n  `make osv-scan OSV_SCAN_IMAGES=\"<image-ref> ...\"` (see\n  `osv-scan-latest-main-images` in the Makefile for the canonical\n  invocation against rolling-main images), but that scans\n  **already-built published images**, not the just-bumped working\n  tree. Mention this if the user asks why a \"fixed\" CVE still\n  appears locally.\n\nIf the user says \"defer\", stop here. If they say \"go\", treat the\nimage flow as a separate planning round — start a new plan with\nDockerfile-level actions (do not extend the Go-mod plan with image\nactions, they live in different files and the decision tree above\ndoes not apply).\n\n## Edge cases worth knowing\n\n- **Pseudo-version on the fix side**: rare, but if a fix only exists at\n  a non-tagged commit, `go get module@<sha>` works. Mark this clearly\n  in the plan so the user is not surprised by a pseudo-version in\n  go.mod.\n- **Module renamed/moved**: if the advisory's module path no longer\n  exists upstream, a `replace` directive may be necessary. Treat this\n  as ambiguous and flag it for the user instead of writing the replace\n  silently.\n- **Multiple CVEs on one module**: combine into a single `go get` to\n  the highest required version. List them as separate rows in the plan\n  table but mark \"(combined bump)\" in the action column.\n- **Backport branches**: when scanning `v2.2.x` etc., the supported\n  upgrade window for a given module may be narrower (e.g. cannot move\n  to a new major). Default to the smallest version that resolves the\n  CVE within the same major.\n- **Topic branch with no alerts on the base branch**: possible if the\n  branch was just rebased after a recent scan. Tell the user; offer to\n  re-run after CI uploads fresh results.\n\n## Files this skill touches\n\n- `go.mod` / `go.sum` — version bumps\n- `osv-scanner.toml` — false-positive ignore entries (root-level; this\n  is the only scanner ignore file in this repo as of writing — `.snyk`,\n  `.grype.yaml`, `.trivyignore` are not present)\n- License attribution files, if `make generate-licenses` is wired up\n\n## Bundled scripts\n\n- `scripts/detect_base.sh` — prints the supported branch that the\n  current HEAD is most likely based on. Used in phase 1.\n\n## Things not to do\n\n- Do **not** commit or stage. The user does that.\n- Do **not** edit go.mod by hand — always go through `go get` so\n  go.sum stays consistent.\n- Do **not** bump a [protected dependency](#protected-dependencies)\n  without an explicit, in-conversation yes from the user — not in a\n  batch, not as a transitive pulled in by a parent bump. Surface it\n  for approval and wait.\n- Do **not** invent a workaround for a CVE with no upstream fix.\n  Do not pin to a fork, do not silence it via `osv-scanner.toml`.\n  List it in the no-fix bucket and move on.\n- Do **not** stop after the source bumps — phase 8 (image\n  follow-up) is part of the workflow. Prompt the user about\n  remaining image findings every time.\n- Do **not** add an `osv-scanner.toml` entry just to silence noise.\n  The reason field has to defend the entry to a future reader.\n- Do **not** run `make generate-all` or other heavy codegen targets\n  for a dep bump — they are not needed for non-API changes and they\n  slow the loop down by ~30s for no benefit.\n- Do **not** scan an unsupported branch (e.g. `chandler/cveskill`)\n  directly via `osvtool` — there are no GitHub-uploaded alerts for it.\n  (Note: `make osv-scan` does work on any branch because it runs the\n  scanner locally; that is fine for verification.)\n- Do **not** reach for Python, Node, or any other parser. Use `jq`\n  and `yq` — see the Tooling section.\n- Do **not** invoke `osv-scanner` directly. Use `make osv-scan` — it\n  runs the scanner in Docker with the right config and output paths.\n- Do **not** dump the default YAML summary into the chat for the\n  cross-branch landscape view. Use `./hack/osvtool --table` (phase 1)\n  — it's a single small markdown table instead of dozens of lines of\n  nested YAML.\n\n<!-- File: pkg/kgateway/extensions2/plugins/waypoint/README.md (Tokens: ~439 | Category: plugin-manifest) -->\n# Kgateway Istio Waypoint Integration\n\n**Note:** This document serves as a reference for some of key design decisions related to Kgateway Istio Waypoint integration. Rather than creating extensive documentation for each decision, this file collects important decision points to provide a centralized reference.\n\n## Authorization Policy Namespace Configuration\n\n### Overview\nThis directory contains the implementation for integrating Kgateway with Istio Waypoints, including support for waypoint authorization policies through GatewayClass as a TargetRef.\n\n### Root Namespace for Authorization Policies\nWhen implementing GatewayClass as a TargetRef for waypoint authorization policies, we needed to determine which namespace(s) should be used for global policies. After discussion among maintainers, the following decisions were made:\n\n1. **Default Root Namespace**: The default root namespace for waypoint authorization policies will be `istio-system` to maintain consistency with Istio's implementation.\n\n2. **Configurability**: The root namespace is configurable through Settings, allowing users to change it if needed.\n\n3. **Setting Name**: The configuration setting will be called \"Mesh root namespace\" to clearly indicate its purpose.\n\n### Rationale\n- Maintaining consistency with Istio's implementation reduces confusion for users, especially during migration.\n- The `istio-system` namespace is typically the permanent root namespace for mesh configurations.\n- Waypoint configurations should be consistent with ztunnel regardless of whether using Istio or kgateway.\n- Having a single configurable namespace (rather than multiple namespaces) avoids potential conflicts between duplicate authorization policies.\n\n### Usage\nAuthorization policies targeting waypoints should be placed in the `istio-system` namespace by default, or in the configured mesh root namespace if changed via Settings.\n\n```yaml\n# Example authorization policy in istio-system namespace\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: waypoint-policy\n  namespace: istio-system\nspec:\n  targetRef:\n    kind: GatewayClass\n    name: kgateway-waypoint\n  # Policy details...\n```\n\nFor users migrating from Istio to KGateway, using the default `istio-system` namespace simplifies the transition by allowing existing authorization policies to work with KGateway waypoints.\n\n<!-- File: tools/build-tools/README.md (Tokens: ~578 | Category: plugin-manifest) -->\n## build-tools (Codespaces/devcontainer image)\n\nThis directory contains the Docker image definition for the `build-tools` devcontainer used by this repo.\n\nIt is **inspired by Istio's `build-tools` image** (from `istio/common-files`) and is intended to be\npublished to GitHub Container Registry (GHCR) so GitHub Codespaces can pull it quickly and reliably.\n\n### What’s included (high level)\n\n- Go (version matches `go.mod`)\n- Rust toolchain (for `internal/envoy_modules/`)\n- Common build tooling: `git`, `make`, `gcc`, `jq`, `yq`, `kubectl`, `kind`, `helm`, `protoc`, `buf`\n- Docker CLI (for `docker-outside-of-docker` feature)\n- `vim` (for editing) \n\n### Building locally\n\nYou can build the image locally with the make target:\n```bash\nmake build-tools-image\n```\n\nTest the build container locally:\n```shell\ndocker run -it -v \"$(pwd):/workspace\" -w /workspace kgateway-build-tools:dev\n```\n\nYou should be able to run kgateway commands (ie. `make generate-all`, `make run`, etc.) from within the container.\n\n### Testing as GitHub Codespaces would build it\n\nGitHub Codespaces uses the `@devcontainers/cli` to build and run the devcontainer. To reproduce\nexactly what Codespaces does (Dockerfile + devcontainer features like `docker-outside-of-docker`\nand `kind`), use the `devcontainer` CLI:\n\n```bash\n# Install the CLI (one-time)\nnpm install -g @devcontainers/cli\n\n# Build the image from the repo root (this is what Codespaces runs)\ndevcontainer build --workspace-folder .\n```\n\nThis builds the Dockerfile **and** installs the devcontainer features defined in\n`.devcontainer/devcontainer.json`, producing the same image a Codespace would use.\n\nTo run an interactive shell in the built image:\n```bash\ndevcontainer up --workspace-folder .\ndevcontainer exec --workspace-folder . bash\n```\n\n> **Note:** `make build-tools-image` only builds the Dockerfile and does **not** apply\n> devcontainer features. Use `devcontainer build` when you need to verify that features\n> (Docker CLI version pinning, kind, etc.) are configured correctly.\n\n### Using the devcontainer with VS Code\n\n1. Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.\n2. Open the root of this repo in VS Code.\n3. Click the \"Reopen in Container\" button.\n\nYou should now be able run kgateway commands inside the devcontainer instead of having to install dependencies locally.\n</yakaai_skills>"}