# Cve Fix

> Fix CVEs in Submariner Go repositories. Supports parallel execution across multiple repos and branches. Arguments are optional and order-independent. Use when this capability is needed.

- Skill: `tomevault-io/cve-fix` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/cve-fix`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/cve-fix/raw
- Safety review: WARNING (external: skill-scanner PASS, skillspector FAIL)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/cve-fix

---


# CVE Fix Workflow

Fix CVEs in Go dependencies. One commit per package, one PR total.

**Usage:**

- `/cve-fix` - current repo, current branch
- `/cve-fix 0.23` - current repo, specified branch (short form)
- `/cve-fix ../submariner-operator` - specified repo, current branch
- `/cve-fix release-0.23 ../submariner-operator` - both specified (order doesn't matter)

**Arguments** (both optional, order-independent):

- `branch`: Branch name (anything that's not a path). Short versions like `0.23` auto-expand to `release-0.23`.
- `repo`: Path to repository (starts with `/`, `./`, `../`, `~/`, or is existing directory)

**Working with multiple repositories:** Accepts repository and branch arguments to fix CVEs across different repositories:

```bash
/cve-fix release-0.23 ../submariner-operator
/cve-fix ../lighthouse release-0.23
/cve-fix release-0.23 ../admiral
/cve-fix devel ../subctl
```

Each invocation is independent (isolated execution context). Multiple invocations run sequentially - one completes before
the next starts. Cannot work on the same repository directory simultaneously.

---

## Step 0: Detect Repository Configuration

Run detection and display results before proceeding.

```bash
# Parse arguments: [repo] [branch] in any order
read -r ARG1 ARG2 REST <<<"$ARGUMENTS"

if [[ -n "$REST" ]]; then
  echo "ERROR: Too many arguments. Usage: /cve-fix [repo] [branch]"
  exit 1
fi

# Classify arguments: path (/, ./, ../, ~/) or existing dir = repo, otherwise = branch
REPO=""
BRANCH=""

for arg in "$ARG1" "$ARG2"; do
  [[ -z "$arg" ]] && continue

  # Expand tilde for home directory paths
  arg="${arg/#\~/$HOME}"

  if [[ "$arg" == /* ]] || [[ "$arg" == ./* ]] || [[ "$arg" == ../* ]] || [[ -d "$arg" ]]; then
    [[ -n "$REPO" ]] && { echo "ERROR: Multiple repositories specified"; exit 1; }
    REPO="$arg"
  else
    [[ -n "$BRANCH" ]] && { echo "ERROR: Multiple branches specified"; exit 1; }
    BRANCH="$arg"
  fi
done

# Apply defaults
REPO="${REPO:-.}"

# Validate repo exists and is git repo
if [[ ! -d "$REPO" ]]; then
  echo "ERROR: Repository not found: $REPO"
  exit 1
fi

if ! git -C "$REPO" rev-parse --git-dir &>/dev/null; then
  echo "ERROR: Not a git repository: $REPO"
  exit 1
fi

# Get current branch if not specified
if [[ -z "$BRANCH" ]]; then
  BRANCH=$(git -C "$REPO" branch --show-current 2>/dev/null)
  if [[ -z "$BRANCH" ]]; then
    echo "ERROR: Not on a branch (detached HEAD). Specify branch explicitly."
    exit 1
  fi
  echo "Using current branch: $BRANCH"
fi

# Normalize short version to full branch name (0.22 → release-0.22)
if [[ "$BRANCH" =~ ^[0-9]+\.[0-9]+$ ]]; then
  BRANCH="release-${BRANCH}"
  echo "Normalized to branch: $BRANCH"
fi

# Change to repository directory
if [[ "$REPO" != "." ]]; then
  cd "$REPO" || {
    echo "ERROR: Cannot change to directory: $REPO"
    exit 1
  }
  echo "Working in repository: $REPO"
fi

# tools/go.mod presence
HAS_TOOLS_GOMOD=false
test -f tools/go.mod && HAS_TOOLS_GOMOD=true

# Generated files with version comments
GENERATED_FILE=""
DIFF_IGNORE_ARGS=""

if grep -rql "controller-gen.kubebuilder.io/version" --include="*.go" . 2>/dev/null; then
  DIFF_IGNORE_ARGS="-Icontroller-gen.kubebuilder.io/version"
  GENERATED_FILE=$(grep -rl "controller-gen.kubebuilder.io/version" --include="*.go" . 2>/dev/null | head -1)
elif find . -name "*.pb.go" -type f 2>/dev/null | head -1 | grep -q .; then
  DIFF_IGNORE_ARGS="-I^//"
  GENERATED_FILE=$(find . -name "*.pb.go" -type f 2>/dev/null | head -1)
fi

# Pre-scan build requirement (UPX compression)
NEEDS_BUILD_FOR_SCAN=false
grep -q "BUILD_UPX" Makefile 2>/dev/null && NEEDS_BUILD_FOR_SCAN=true

# Container runtime (docker or podman)
CONTAINER_CMD=""
if command -v docker &>/dev/null && docker info &>/dev/null; then
  CONTAINER_CMD="docker"
elif command -v podman &>/dev/null && podman info &>/dev/null; then
  CONTAINER_CMD="podman"
fi

# Local grype
HAS_LOCAL_GRYPE=false
command -v grype &>/dev/null && HAS_LOCAL_GRYPE=true

# Shipyard build image (legacy artifact name: shipyard-dapper-base)
SHIPYARD_IMAGE=""
SHIPYARD_GO_VERSION=""

# Detect base branch to determine image tag
if [[ "$BRANCH" == "devel" ]]; then
  SHIPYARD_TAG="devel"
elif [[ "$BRANCH" =~ ^release- ]]; then
  SHIPYARD_TAG="$BRANCH"
else
  echo "WARNING: Unknown branch pattern, assuming devel build image"
  SHIPYARD_TAG="devel"
fi

SHIPYARD_IMAGE="quay.io/submariner/shipyard-dapper-base:${SHIPYARD_TAG}"

# Pull latest image and check Go version
if [[ -n "$CONTAINER_CMD" ]]; then
  echo "Checking Shipyard build image: $SHIPYARD_IMAGE"
  $CONTAINER_CMD pull "$SHIPYARD_IMAGE" 2>&1 | tail -2
  SHIPYARD_GO_VERSION=$($CONTAINER_CMD run --rm "$SHIPYARD_IMAGE" go version 2>/dev/null || echo "unknown")
  echo "Shipyard Go version: $SHIPYARD_GO_VERSION"
fi
```

Display configuration: tools/go.mod presence, generated file handling, build requirements, available scanners, Shipyard build image,
and Go version.

---

## Step 1: Branch Setup

```bash
# Save original branch/commit to restore on early exit
ORIGINAL_REF=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [[ "$ORIGINAL_REF" == "HEAD" ]]; then
  # Detached HEAD - save the commit hash instead
  ORIGINAL_REF=$(git rev-parse HEAD)
fi

# Track fetch status for summary
FETCH_FAILED=false

if ! git fetch 2>/dev/null; then
  echo "WARNING: git fetch failed. Continuing with cached remote state. Run 'git fetch' manually and re-run if it fetches updates."
  FETCH_FAILED=true
fi

# Create fix branch directly from origin (bypasses local branch state)
DATE=$(date +%Y-%m-%d)
VERSION=$(echo "$BRANCH" | sed 's/release-//')
FIX_BRANCH="fix-${VERSION}-cves-${DATE}"

# Add suffix if branch exists (-v2, -v3, etc.)
SUFFIX=""
while git show-ref --verify --quiet refs/heads/"${FIX_BRANCH}${SUFFIX}"; do
  if [[ -z "$SUFFIX" ]]; then SUFFIX="-v2"; else NUM=${SUFFIX#-v}; SUFFIX="-v$((NUM+1))"; fi
done

if ! git checkout -b "${FIX_BRANCH}${SUFFIX}" origin/"$BRANCH" 2>/dev/null; then
  echo "ERROR: Could not create fix branch from origin/$BRANCH. Branch may not exist remotely."
  exit 1
fi
```

---

## Step 2: Clean Build State

Remove all build artifacts to ensure clean scan:

```bash
# Remove binary artifacts and build cache
rm -rf ./bin ./dist ./output

# Run make clean to remove all generated/ignored files
# This runs in Shipyard build container and cleans comprehensively
make clean 2>&1 | grep -v "Error.*ignored" || true

# Verify critical directories are clean
if [ -d "./bin" ] && [ "$(ls -A ./bin 2>/dev/null)" ]; then
  echo "WARNING: ./bin still contains files after cleanup"
  ls -la ./bin
fi
```

This ensures grype scans only source code and go.mod files, not stale binaries.

---

## Step 3: Scan for CVEs

```bash
# Build if needed (submariner with UPX compression)
if [[ "$NEEDS_BUILD_FOR_SCAN" == "true" ]]; then
  make BUILD_UPX=false build
fi

# Scan with container runtime or local grype
if [[ -n "$CONTAINER_CMD" ]]; then
  $CONTAINER_CMD volume create grype-db && \
  $CONTAINER_CMD run --pull=always --rm -v grype-db:/root/.cache/grype anchore/grype:latest db update && \
  $CONTAINER_CMD run --rm -v grype-db:/root/.cache/grype -v "$(pwd)":/src anchore/grype:latest /src --config /src/.grype.yaml -o table
elif [[ "$HAS_LOCAL_GRYPE" == "true" ]]; then
  grype db update && grype . --config .grype.yaml -o table
else
  echo "ERROR: No scanner available. Either:"
  echo "  1. Install docker or podman (grype runs in container, no local install needed)"
  echo "  2. Install grype locally: curl -sSfL https://get.anchore.io/grype | sudo sh -s -- -b /usr/local/bin"
fi
```

Ignore warning: `[0000]  WARN no explicit name and version provided for directory source, deriving artifact ID from the given
path (which is not ideal)`

**Check scan results:**

If output shows "No vulnerabilities found", clean up and exit:

```bash
# If no CVEs found, delete the fix branch and exit
FIX_BRANCH_FULL=$(git rev-parse --abbrev-ref HEAD)
git checkout "$ORIGINAL_REF"
git branch -D "$FIX_BRANCH_FULL"
echo "No CVEs found in $BRANCH - branch is clean"
exit 0
```

For each CVE found, note **NAME** (package), **FIXED-IN** (version), and **VULNERABILITY** (CVE ID). Same package may
appear multiple times with different versions (e.g., v1.2.3 in tools, v1.3.0 in main); treat each as separate fix.

**Stdlib CVEs** (NAME=stdlib): Can often be fixed by updating the `go` directive in go.mod to require a minimum Go version with the
fix. If go.mod update doesn't resolve the CVE (because the Shipyard build container has an older Go), then a Fedora version update
in Shipyard is needed.

---

## Step 4: Locate Package

```bash
PACKAGE="[package-from-scan]"

# Find in go.mod files
if [[ "$HAS_TOOLS_GOMOD" == "true" ]]; then
  grep -Fl "$PACKAGE" go.mod tools/go.mod 2>/dev/null
else
  grep -Fl "$PACKAGE" go.mod 2>/dev/null
fi

# Check for replace directives
grep "replace.*$(basename "$PACKAGE")" go.mod tools/go.mod 2>/dev/null
```

**If replace directive found**, check git history to understand why it was added:

```bash
git log -p --all -G "replace.*$(basename "$PACKAGE")" -- go.mod tools/go.mod | head -50
```

**If obsolete** (no longer needed based on git history), remove it:

```bash
go mod edit -dropreplace="$PACKAGE"
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && go -C tools mod edit -dropreplace="$PACKAGE"
```

**Otherwise** (still needed), the replace directive will be updated to a safe version in Step 5.

**Check parent-child dependencies** (fix parent first if both have CVEs):

```bash
go mod graph | grep "$PACKAGE"
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && go -C tools mod graph | grep "$PACKAGE"
```

---

## Step 5: Update Package

```bash
PACKAGE="[package]"
VERSION="[fixed-version]"  # Use highest if multiple CVEs

# Update in tools/go.mod if present there
if grep -q "$PACKAGE" tools/go.mod 2>/dev/null; then
  go -C tools get "${PACKAGE}@v${VERSION}" && go -C tools mod tidy
fi

# Update in go.mod if present there
if grep -q "$PACKAGE" go.mod 2>/dev/null; then
  go get "${PACKAGE}@v${VERSION}" && go mod tidy
fi

# Clean up go.mod artifacts (portable sed -i for GNU/BSD)
sed -i.bak '/^toolchain/d' go.mod && rm -f go.mod.bak
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && sed -i.bak '/^toolchain/d' tools/go.mod && rm -f tools/go.mod.bak
sed -i.bak '/^$/{N;/^\n$/s/\n//;}' go.mod && rm -f go.mod.bak
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && sed -i.bak '/^$/{N;/^\n$/s/\n//;}' tools/go.mod && rm -f tools/go.mod.bak

# Verify changes
git diff $DIFF_IGNORE_ARGS
```

Expected: Dependency file updates only.

**On stable branches:** If go get upgrades Go (1.X→1.Y) or K8s (v0.A→v0.B) minor version:

- Low CVEs: revert changes, add to ignore list (Step 10), and commit. Note in summary for user awareness.
- Medium/High/Critical CVEs: revert changes, do NOT ignore automatically. Note in summary and flag for team review.

### Stdlib CVE Variant

For stdlib CVEs, update the `go` directive instead of a package:

```bash
# Determine required Go version from CVE scan FIXED-IN column
# Example: "1.24.12" from "*1.24.12, 1.25.6"
GO_VERSION="[version-from-FIXED-IN]"

# Update go directive in go.mod
go mod edit -go="${GO_VERSION}"
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && go -C tools mod edit -go="${GO_VERSION}"

# Run go mod tidy to update dependencies
go mod tidy
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && go -C tools mod tidy

# Clean up toolchain directive and extra blank lines (portable sed -i for GNU/BSD)
sed -i.bak '/^toolchain/d' go.mod && rm -f go.mod.bak
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && sed -i.bak '/^toolchain/d' tools/go.mod && rm -f tools/go.mod.bak
sed -i.bak '/^$/{N;/^\n$/s/\n//;}' go.mod && rm -f go.mod.bak
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && sed -i.bak '/^$/{N;/^\n$/s/\n//;}' tools/go.mod && rm -f tools/go.mod.bak

# Verify changes
git diff $DIFF_IGNORE_ARGS
```

Expected: go.mod shows `go 1.24.0` → `go 1.24.12` (example).

**If CVE persists after go.mod update**: The Shipyard build container's Go version (from Step 0) is older than required. This means:

- Local scans after `rm -rf ./bin` will pass (no binaries to scan)
- CI builds will still fail because Shipyard build container compiles with old Go
- A Shipyard Fedora update is needed to bring newer Go to the build image

For commit message format:

```text
Bump Go to [version] for stdlib CVEs

Updates Go requirement from [old-version] to [new-version] to address
stdlib vulnerabilities.

Fixes: [CVE-ID-1], [CVE-ID-2], ...
```

---

## Step 6: Clean Build Artifacts

```bash
make clean
```

Removes build artifacts to avoid false positives in rescan. Network errors: see Common Issues.

---

## Step 7: Verify Fix

```bash
if [[ "$NEEDS_BUILD_FOR_SCAN" == "true" ]]; then
  make BUILD_UPX=false build
fi

if [[ -n "$CONTAINER_CMD" ]]; then
  $CONTAINER_CMD run --rm -v grype-db:/root/.cache/grype -v "$(pwd)":/src anchore/grype:latest /src --config /src/.grype.yaml -o table
elif [[ "$HAS_LOCAL_GRYPE" == "true" ]]; then
  grype . --config .grype.yaml -o table
fi
```

CVE for this package should no longer appear.

**If CVE persists**: Double-check you used the correct version from Step 3 FIXED-IN column. If version is correct but CVE
persists, recheck Step 4 for replace directives.

---

## Step 8: Verify Build

```bash
make unit
```

This runs unit tests in the Shipyard build container (unless LOCAL_BUILD=1 is set). The Shipyard Go version was displayed in
Step 0. Skip this step during multi-package fixes; run once at end. Build errors may indicate incompatible dependency versions for
this branch.

**Note**: If tests fail with "go.mod requires go >= X.Y.Z (running go A.B.C)":

- The error shows the **Shipyard build container's** Go version (A.B.C), not your local Go version
- This is a version requirement mismatch, not an actual test failure
- Compare A.B.C with the Shipyard Go version from Step 0 to confirm
- If Shipyard Go is insufficient (A.B.C < X.Y.Z), Shipyard needs updating with newer Fedora

---

## Step 9: Commit

```bash
# Stage dependency files
git add go.mod go.sum
[[ "$HAS_TOOLS_GOMOD" == "true" ]] && git add tools/go.mod tools/go.sum

# Handle generated files (stage only if substantive changes)
if [[ -n "$GENERATED_FILE" ]] && [[ -n "$DIFF_IGNORE_ARGS" ]]; then
  if git diff $DIFF_IGNORE_ARGS "$GENERATED_FILE" 2>/dev/null | grep -q .; then
    git add "$GENERATED_FILE"
  else
    git checkout "$GENERATED_FILE"
  fi
fi

git diff --staged --stat
```

Expected: go.mod, go.sum (and tools versions if applicable, generated file only if substantive changes).

### Commit Message Format

**Single CVE:**

```text
Bump <abbreviated-package> for <CVE-ID>

Full package: <full-package-path>
```

**Multiple CVEs (same package):**

```text
Bump <abbreviated-package> for CVEs

Full package: <full-package-path>
Fixes: <CVE-ID-1>, <CVE-ID-2>
```

**Abbreviations:**

- `github.com/docker/docker` → `docker/docker`
- `golang.org/x/oauth2` → `x/oauth2`
- `helm.sh/helm/v3` → `helm/v3`
- Keep `k8s.io/` prefix

**If only tools files changed:** add "in /tools" to subject.

**For stdlib CVE fixes:**

```text
Bump Go to <version> for stdlib CVEs

Updates Go requirement from <old-version> to <new-version> to address
stdlib vulnerabilities.

Fixes: <CVE-ID-1>, <CVE-ID-2>, <CVE-ID-3>
```

Example:

```text
Bump Go to 1.24.12 for stdlib CVEs

Updates Go requirement from 1.24.0 to 1.24.12 to address
stdlib vulnerabilities.

Fixes: CVE-2025-61726, CVE-2025-61727, CVE-2025-61728, CVE-2025-61729, CVE-2025-61730, CVE-2025-61731
```

```bash
git commit -s -m "$(cat <<'EOF'
Bump [abbreviated-package] for [CVE-ID]

Full package: [full-package-path]
EOF
)"
```

**After each commit, rebuild (if NEEDS_BUILD_FOR_SCAN) and rescan** to catch newly introduced CVEs, then repeat Steps 4-9.

---

## Step 10: Ignore Unfixable CVEs

Skip if no CVEs remain.

**Always prefer fixing over ignoring.** Even low-severity CVEs appear in user security scanners. Only ignore when fixing
is not possible for this branch.

**Stdlib CVEs**: Do not ignore. First attempt to fix by updating go.mod (see Step 5 stdlib variant above). If Shipyard's
Go version is too old (check Step 0), flag for Shipyard Fedora update. Note in summary.

**Autonomous decision criteria:**

- Attempt fix first
- Low CVEs requiring breaking changes: ignore and commit, note in summary
- Medium/High/Critical requiring breaking changes: flag for review, do NOT ignore

Add to existing `ignore:` list in `.grype.yaml`:

```yaml
  # Update requires [incompatibility]. [Severity] doesn't justify breaking changes.
  - vulnerability: GHSA-xxxx-xxxx-xxxx
    package:
      name: package.name/path
```

```bash
git add .grype.yaml
git commit -s -m "$(cat <<'EOF'
Ignore [package] CVEs incompatible with release-X.Y

[Package] CVEs require versions with [incompatible dependency]
incompatible with this branch's [current dependency].
EOF
)"
```

---

## Step 11: Final Verification

```bash
# Check if any commits were made
COMMIT_COUNT=$(git log origin/"$BRANCH"..HEAD --oneline 2>/dev/null | wc -l)

if [[ "$COMMIT_COUNT" -eq 0 ]]; then
  echo "No commits made - no CVEs were fixed."
  FIX_BRANCH_FULL=$(git rev-parse --abbrev-ref HEAD)
  git checkout "$ORIGINAL_REF"
  git branch -D "$FIX_BRANCH_FULL"
  echo "Deleted empty fix branch: $FIX_BRANCH_FULL"
  exit 0
fi

# Rebuild if needed
if [[ "$NEEDS_BUILD_FOR_SCAN" == "true" ]]; then
  make BUILD_UPX=false build
fi

# Final scan
if [[ -n "$CONTAINER_CMD" ]]; then
  $CONTAINER_CMD run --rm -v grype-db:/root/.cache/grype -v "$(pwd)":/src anchore/grype:latest /src --config /src/.grype.yaml -o table
elif [[ "$HAS_LOCAL_GRYPE" == "true" ]]; then
  grype . --config .grype.yaml -o table
fi

# Only run tests if we have commits to verify
make unit  # Runs in Shipyard build container
git log origin/"$BRANCH"..HEAD
git diff $DIFF_IGNORE_ARGS
```

Expected: No vulnerabilities, tests pass, clean diff.

---

## Step 12: Create Pull Request

**Note:** Git push and PR creation require SSH authentication, which may fail if keys are on external devices (YubiKey, hardware tokens).

Extract variables:

```bash
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
BASE_BRANCH=$(echo "$CURRENT_BRANCH" | sed 's/fix-\([0-9.]*\)-.*/release-\1/; s/fix-devel-.*/devel/')
COMMIT_COUNT=$(git log "origin/${BASE_BRANCH}"..HEAD --oneline | wc -l)
PLURAL=$([[ "$COMMIT_COUNT" -eq 1 ]] && echo "" || echo "s")
FORK_REMOTE=$(git remote -v | awk '!/submariner-io/ && /\(push\)/ { print $1; exit }')
FORK_USER=$(git remote get-url "${FORK_REMOTE}" 2>/dev/null | sed -E 's#.*github.com[:/]+([^/]+)/.*#\1#')
```

Substitute into template and provide in response (not bash output):

```bash
git push <FORK_REMOTE> <CURRENT_BRANCH> && \
gh pr create \
  --title "Fix CVE<PLURAL> in <BASE_BRANCH>" \
  --body "See commit message<PLURAL> for details." \
  --base "<BASE_BRANCH>" \
  --head "<FORK_USER>:<CURRENT_BRANCH>" \
  --assignee "@me"
```

---

## Summary (Return Value)

When complete, provide a summary including:

1. **Repository**: Path to repository (if not current directory, omit if ".")
2. **Branch**: Target branch (and fix branch name if created)
3. **CVEs Fixed**: List of CVE IDs with package names (or "None - branch is clean" if no CVEs found)
4. **CVEs Ignored**: List with reasons (if any)
5. **CVEs Needing Review**: Medium/High/Critical that couldn't be fixed (if any)
6. **Stdlib CVEs**: Note if fixed via go.mod update; flag if Shipyard update needed
7. **PR Command**: The substituted command from Step 12 (only if commits were made)
8. **Status**: Success, partial success, or blocked
9. **Warnings**: If FETCH_FAILED=true, warn that branch was created from cached remote state and advise running git fetch and
   re-running if updates are fetched

**Note**: Skill exits early in Step 3 if no CVEs found, or in Step 11 if all CVEs were handled without commits.

Example:

```text
## CVE Fix Complete: release-0.23

**Repository:** ../submariner-operator
**Branch:** fix-0.23-cves-2026-02-09
**Status:** Success

### Fixed (3 packages + stdlib)
- CVE-2025-61726, CVE-2025-61729, CVE-2025-61731: stdlib (go.mod updated to 1.24.12)
- GHSA-xxxx-xxxx-xxxx: golang.org/x/net
- GHSA-yyyy-yyyy-yyyy: github.com/docker/docker

### Requires Shipyard Update
- Stdlib CVEs fixed in go.mod, but Shipyard build image has Go 1.24.11. Recommend updating Shipyard to Fedora 42+ for Go 1.24.12
  to ensure CI passes.

### PR Command
git push origin fix-0.23-cves-2026-02-09 && gh pr create ...

### Warnings
⚠️  git fetch failed. Branch created from cached remote state. Run 'git fetch' and re-run if it fetches new commits.
```

---

## Common Issues

| Issue | Solution |
| ----- | -------- |
| CVE persists after fix | Verify FIXED-IN version; check for replace directives in Step 4 |
| New CVE appears after fix | Dependency downgrade introduced it; fix immediately |
| Tests fail | Try different version; check CI logs |
| Large dependency updates (Helm, etc.) | May break old branches; check Go/K8s compatibility |
| Container "no route to host" | Run `sudo systemctl restart docker` or `sudo systemctl restart podman` |
| Stdlib CVEs | Update go directive in go.mod. If CVE persists after cleaning, check Shipyard Go version (Step 0). May need Fedora update. |
| Old binaries causing false CVEs | Cleaned automatically in Step 2. For manual cleanup, run `make clean` |
| Git fetch fails | Run `git fetch` before starting to get latest commits. Skill continues with cached state if fetch fails |

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/submariner-io) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

