Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions skills/govulncheck-source/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
name: govulncheck-source
description: Run govulncheck against Go packages in bottlerocket-core-kit using the bottlerocket-sdk container
---

# Skill: Govulncheck Source

**Keywords:** govulncheck, go, vulnerability, security, scanning, core-kit, packages, sdk, docker

## Purpose

Scan Go packages in `bottlerocket-core-kit` for known vulnerabilities using `govulncheck` in source mode.
The scan runs inside the `bottlerocket-sdk` Docker container, using the correct Go version for each package.

## When to Use

- Auditing Go dependencies for known vulnerabilities before a release
- Checking if a package update introduces or resolves vulnerabilities
- Periodic security review of Go packages in core-kit

## Prerequisites

Before starting, verify:
- Docker is installed and running (`docker info`)
- Python 3.11+ is available (`python3 --version`) — needed for `tomllib`
- You are working from within a grove directory
- `kits/bottlerocket-core-kit` exists in the grove

## Input

The user provides:
- **Package name(s)** to scan (e.g., `runc`, `containerd-2.1`)
- **Kit name** — defaults to `bottlerocket-core-kit`

## Procedure

### 1. Pull the SDK image

```bash
python3 skills/govulncheck-source/scripts/pull-sdk.py kits/<kit-name>/Twoliter.toml
```

This outputs two lines to stdout:
1. The fully-qualified SDK image (e.g., `public.ecr.aws/bottlerocket/bottlerocket-sdk:v0.70.0`)
2. The default Go version in the SDK (e.g., `1.24`)

Capture both values — they are needed by subsequent steps.

### 2. Extract package metadata

For each package, run:

```bash
bash skills/govulncheck-source/scripts/extract-metadata.sh \
<sdk-image> <default-go-major> kits/<kit-name>/packages <package-name>
```

This outputs a single tab-separated line: `version\turl\tgo_major\tgitrev`

- `version` — upstream version (e.g., `1.2.8`)
- `url` — upstream repository URL (e.g., `https://github.com/opencontainers/runc`)
- `go_major` — Go toolchain version to use (e.g., `1.24`)
- `gitrev` — commit hash if the package is pinned to a specific commit (may be empty)

### 3. Determine source type and obtain source

Read `kits/<kit-name>/packages/<package-name>/Cargo.toml` to determine the source type:

**Upstream packages** (have `[[package.metadata.build-package.external-files]]`):
Clone the upstream repo at the correct ref.
Try tags in this order:
1. `v<version>` (most common)
2. `<version>` (bare)
3. For moby/docker-engine: `docker-v<version>`
4. Fall back to `gitrev` commit hash if tags fail

```bash
git clone --depth 1 --branch v<version> <url> /tmp/govulncheck-<package>
```

**Internal packages** (have `source-groups = ["<group>"]`):
Source lives in `kits/<kit-name>/sources/<group>/`.
No cloning needed — use the local path directly.

### 4. Run govulncheck

```bash
bash skills/govulncheck-source/scripts/run-govulncheck.sh \
<sdk-image> <go-major> <source-dir>
```

Save the output to `govulncheck-results/<package>/govulncheck.txt`.

Count vulnerabilities by looking for `Vulnerability #` lines in the output.

### 5. Clean up and report

Remove any cloned source directories.

Present results as a summary table:

| Package | Version | Go | Vulns | Status |
|---------|---------|-----|-------|--------|
| runc | 1.2.8 | 1.24 | 0 | clean |

## Output Structure

```
govulncheck-results/
├── <package>/
│ └── govulncheck.txt
└── ...
```

## Common Issues

**Docker not running:**
Start Docker with `sudo systemctl start docker`

**SDK image pull fails (ECR auth):**
```bash
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
```

**govulncheck reports "no go.mod":**
Some upstream repos have `go.mod` in a subdirectory.
The `run-govulncheck.sh` script searches up to 3 levels deep automatically.

**Tag not found during clone:**
Try the next tag variant, then fall back to full clone with `gitrev` checkout.
35 changes: 35 additions & 0 deletions skills/govulncheck-source/scripts/extract-metadata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Extract package metadata (version, url, go_major, gitrev) via rpmspec in the SDK container.
#
# Usage:
# ./extract-metadata.sh <sdk-image> <default-go-major> <packages-dir> <package-name>
#
# Outputs a single line of tab-separated values:
# version\turl\tgo_major\tgitrev
set -euo pipefail

SDK_IMAGE="$1"
DEFAULT_GO_MAJOR="$2"
PACKAGES_DIR="$3"
PKG="$4"

docker run --rm \
-v "$(cd "$PACKAGES_DIR" && pwd):/packages:ro" \
"$SDK_IMAGE" bash -c "
cp /usr/lib/rpm/platform/x86_64-bottlerocket/macros ~/.rpmmacros
spec='/packages/${PKG}/${PKG}.spec'

version=\$(rpmspec --query --queryformat='%{version}\n' \"\$spec\" 2>/dev/null | head -1)
url=\$(rpmspec --query --queryformat='%{url}\n' \"\$spec\" 2>/dev/null | head -1)

expanded=\$(rpmspec -P \"\$spec\" 2>/dev/null)
go_major=\$(echo \"\$expanded\" | grep -oP 'GO_MAJOR=\"\K[^\"]+' | head -1)
go_major=\${go_major:-${DEFAULT_GO_MAJOR}}

gitrev=\$(echo \"\$expanded\" | grep '^Source0:' | grep -oE '[0-9a-f]{40}' | head -1)
if [[ -z \"\$gitrev\" ]]; then
gitrev=\$(grep 'gitrev' \"\$spec\" | grep -oE '[0-9a-f]{40}' | head -1)
fi

printf '%s\t%s\t%s\t%s\n' \"\$version\" \"\$url\" \"\$go_major\" \"\$gitrev\"
"
51 changes: 51 additions & 0 deletions skills/govulncheck-source/scripts/pull-sdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Parse Twoliter.toml for the SDK image and pull it via Docker.

Usage:
python3 pull-sdk.py <path-to-Twoliter.toml>

Outputs the fully-qualified SDK image reference on stdout, e.g.:
public.ecr.aws/bottlerocket/bottlerocket-sdk:v0.70.0

Also prints the lowest Go version available in the SDK on a second line, e.g.:
1.24
"""
import subprocess
import sys

try:
import tomllib
except ModuleNotFoundError:
import tomli as tomllib # type: ignore[no-redef]


def main() -> None:
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <Twoliter.toml>", file=sys.stderr)
sys.exit(1)

with open(sys.argv[1], "rb") as f:
data = tomllib.load(f)

registry = data["vendor"]["bottlerocket"]["registry"]
name = data["sdk"]["name"]
version = data["sdk"]["version"]
image = f"{registry}/{name}:v{version}"

subprocess.run(["docker", "pull", image], check=True,
stdout=sys.stderr, stderr=sys.stderr)

result = subprocess.run(
["docker", "run", "--rm", image, "bash", "-c",
r'ls -d /usr/libexec/go-* | sed "s|.*/go-||" | sort -V | head -1'],
capture_output=True, text=True, check=True,
)
default_go = result.stdout.strip()

# stdout: line 1 = image, line 2 = default go version
print(image)
print(default_go)


if __name__ == "__main__":
main()
35 changes: 35 additions & 0 deletions skills/govulncheck-source/scripts/run-govulncheck.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Run govulncheck inside the SDK container against a local source directory.
#
# Usage:
# ./run-govulncheck.sh <sdk-image> <go-major> <source-dir>
#
# Outputs govulncheck results to stdout.
# Exit code 3 = vulnerabilities found, 0 = clean, other = error.
set -euo pipefail

SDK_IMAGE="$1"
GO_MAJOR="$2"
SRC_DIR="$3"

docker run --rm \
-v "$(cd "$SRC_DIR" && pwd):/src:ro" \
"$SDK_IMAGE" bash -c "
export GOBIN=/tmp/bin
/usr/libexec/go-${GO_MAJOR}/bin/go install golang.org/x/vuln/cmd/govulncheck@latest 2>/dev/null

export PATH=/usr/libexec/go-${GO_MAJOR}/bin:/tmp/bin:\$PATH

gomod_dir=/src
if [[ ! -f /src/go.mod ]]; then
found=\$(find /src -maxdepth 3 -name go.mod -print -quit 2>/dev/null)
if [[ -n \"\$found\" ]]; then
gomod_dir=\$(dirname \"\$found\")
else
echo 'ERROR: no go.mod found' >&2
exit 1
fi
fi

govulncheck -C \"\$gomod_dir\" -show verbose ./... 2>&1
"