Skip to content

Commit c02570a

Browse files
authored
Merge pull request #541 from jacksec-engineer/kiro-additional-hooks
fix: remove pip from runtime image and exclude app/.env from build co…
2 parents 7c98f43 + af2ee6a commit c02570a

8 files changed

Lines changed: 75 additions & 3 deletions

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.github/*
22
.gitignore
3+
app/.env
34
CODE_OF_CONDUCT.md
45
CONTRIBUTING.md
56
docker-compose.yaml
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"enabled": true,
3+
"name": "Container Security Scan",
4+
"description": "After a docker build or docker compose build shell command completes, runs Grype, Trivy, and Semgrep against the newly built image to check for new vulnerabilities matching the CI security.yml pipeline.",
5+
"version": "1",
6+
"when": {
7+
"type": "postToolUse",
8+
"toolTypes": [
9+
"shell"
10+
]
11+
},
12+
"then": {
13+
"type": "askAgent",
14+
"prompt": "A shell command just completed. If it was a `docker build` or `docker compose build` (or `docker-compose build`) command, you MUST run the following security scans against the newly built image before the user proceeds.\n\nFirst, determine the image name/tag that was just built. For this project it is `linkshort` (or `jackseceng/linkshort:latest` if fully tagged). Use the tag from the build command if one was specified.\n\nRun all three of the following scans in sequence:\n\n**1. Grype (only-fixed vulnerabilities, CRITICAL/HIGH/MEDIUM)**\n```\ndocker run --rm -v /var/run/docker.sock:/var/run/docker.sock \\\n anchore/grype:v0.112.0 linkshort --only-fixed\n```\n\n**2. Trivy (CRITICAL, HIGH, MEDIUM)**\n```\ndocker run --rm -v /var/run/docker.sock:/var/run/docker.sock \\\n aquasec/trivy:0.70.0 image \\\n --severity CRITICAL,HIGH,MEDIUM \\\n --ignore-unfixed=true \\\n linkshort\n```\n\n**3. Semgrep SAST (source code)**\n```\ndocker run --rm -v ${workspaceFolder}:/src \\\n semgrep/semgrep:1.161.0 semgrep scan --config auto /src\n```\n\nAfter all scans complete:\n- Report a summary of findings grouped by scanner (tool name, severity, description, file/location).\n- If any CRITICAL or HIGH severity vulnerabilities are found, warn the user clearly and recommend they do not commit or push until the issues are resolved or justified.\n- If no issues are found across all scanners, confirm the image is clean.\n\nIf the shell command was NOT a docker build, do nothing."
15+
},
16+
"workspaceFolderName": "LinkShort",
17+
"shortName": "container-security-scan"
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"enabled": true,
3+
"name": "Conventional Commit Format Check",
4+
"description": "Before a git commit shell command is executed, validates that the commit message follows the Conventional Commits format required by CI. Blocks the commit if the message is invalid.",
5+
"version": "1",
6+
"when": {
7+
"type": "preToolUse",
8+
"toolTypes": [
9+
"shell"
10+
]
11+
},
12+
"then": {
13+
"type": "askAgent",
14+
"prompt": "A shell command is about to be executed. If it is a `git commit` command, extract the commit message from the `-m` flag (or commit message file if `-F` is used) and validate it against the Conventional Commits format BEFORE allowing the commit to proceed.\n\nRequired format:\n```\n<type>(<optional scope>): <description>\n```\n\nAllowed types: `feat`, `fix`, `chore`, `docs`, `refactor`, `build`, `ci`, `style`, `perf`, `test`\n\nValid examples:\n- `feat: add custom URL extension support`\n- `fix: handle null response from turso on get_link`\n- `chore(deps): pin cryptography to 47.0.0`\n- `docs: update SRI hash regeneration instructions`\n\nIf the commit message does NOT match this format, block the commit and tell the user exactly what is wrong and how to fix it. Do not proceed until the message is valid.\n\nIf the command is not a `git commit`, allow it to proceed immediately without any checks."
15+
}
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"enabled": true,
3+
"name": "Run Security Scans (Manual)",
4+
"description": "Manually triggered hook to run all three container security scanners (Grype, Trivy, Semgrep) against the local linkshort image on demand, mirroring the CI security.yml pipeline.",
5+
"version": "1",
6+
"when": {
7+
"type": "userTriggered"
8+
},
9+
"then": {
10+
"type": "askAgent",
11+
"prompt": "The user has manually triggered a full security scan.\n\n## Step 1: Sync scanner versions from security.yml\n\nRead `${workspaceFolder}/.github/workflows/security.yml` and extract the following:\n- The Semgrep image tag from the `container: image:` field in the `semgrep` job (e.g. `semgrep/semgrep:1.161.0`)\n- The Trivy version from the `version` default in `trivy-action` — look up the raw action.yaml at `https://raw.githubusercontent.com/aquasecurity/trivy-action/<COMMIT_SHA>/action.yaml` using the pinned commit SHA from the `trivy-action` step, and read the `default:` value under the `version:` input\n- The Grype version: `anchore/scan-action` is pinned by commit SHA in security.yml, but the `grype-version` input is not passed, so the action resolves the latest Grype release dynamically at CI runtime. Mirror this by running `docker run --rm anchore/grype:latest version 2>&1 | grep '^Version:'` to get the current latest version string\n\nNow compare those three versions against the versions currently hardcoded in this hook file at `${workspaceFolder}/.kiro/hooks/manual-security-scan.kiro.hook` and in `${workspaceFolder}/.kiro/hooks/container-security-scan.kiro.hook`.\n\nIf any version has changed, update BOTH hook files with the new versions (edit the image tags in the docker run commands in the prompt strings), then report which versions were updated. If nothing changed, note that versions are already in sync.\n\n## Step 2: Run the scans\n\nRun all three of the following scans against the local `linkshort` image in sequence, using the versions confirmed/updated in Step 1:\n\n**1. Grype (only-fixed vulnerabilities, fail on medium+)**\n```\ndocker run --rm -v /var/run/docker.sock:/var/run/docker.sock \\\n anchore/grype:v0.112.0 linkshort --only-fixed \\\n --fail-on medium\n```\n\n**2. Trivy (CRITICAL, HIGH, MEDIUM — including unfixed)**\n```\ndocker run --rm -v /var/run/docker.sock:/var/run/docker.sock \\\n aquasec/trivy:0.70.0 image \\\n --severity CRITICAL,HIGH,MEDIUM \\\n --ignore-unfixed=false \\\n linkshort\n```\n\n**3. Semgrep SAST (source code)**\n```\ndocker run --rm -v ${workspaceFolder}:/src \\\n semgrep/semgrep:1.161.0 semgrep scan --config auto /src\n```\n\nAfter all scans complete, produce a summary table of all findings grouped by scanner with columns: File/Location, Description, Severity, Tool. If any CRITICAL or HIGH findings exist, highlight them prominently and recommend they are resolved before committing or pushing. If everything is clean, confirm that clearly."
12+
}
13+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"enabled": true,
3+
"name": "SRI Hash Check on Static Asset Change",
4+
"description": "When a file in app/static/ is saved, reminds to regenerate SRI hashes and update the integrity attributes in all HTML templates before committing.",
5+
"version": "1",
6+
"when": {
7+
"type": "fileEdited",
8+
"patterns": [
9+
"app/static/*"
10+
]
11+
},
12+
"then": {
13+
"type": "askAgent",
14+
"prompt": "A file in app/static/ was just edited. Per project conventions, any change to a static asset requires regenerating its SRI hash and updating the matching `integrity` attribute in all referencing HTML templates under app/templates/.\n\n1. Run the following command to get the updated hashes:\n```\nfor f in app/static/*; do\n echo \"$(basename $f): sha384-$(openssl dgst -sha384 -binary $f | openssl base64 -A)\"\ndone\n```\n2. Update the `integrity` attribute in any `app/templates/*.html` file that references the changed static file(s).\n3. Remind the user to rebuild the container after updating templates, and to purge the Cloudflare cache for changed files in production.\n\nSurface this as a reminder so it is not forgotten before the next commit."
15+
}
16+
}

.kiro/hooks/super-linter-pre-commit.kiro.hook

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
},
1212
"then": {
1313
"type": "askAgent",
14-
"prompt": "A shell command is about to be executed. If it is a `git commit` command, you MUST first run Super Linter using this exact Docker command from the repo root before allowing the commit to proceed:\n\n```\ndocker run --rm -e DEFAULT_BRANCH=origin/main -e VALIDATE_ALL_CODEBASE=true -e VALIDATE_CSS=true -e VALIDATE_DOCKERFILE_HADOLINT=true -e VALIDATE_GITLEAKS=true -e VALIDATE_GITHUB_ACTIONS=true -e VALIDATE_HTML=true -e VALIDATE_MARKDOWN=true -e VALIDATE_PYTHON_BLACK=true -e VALIDATE_PYTHON_ISORT=true -e VALIDATE_YAML=true -e VALIDATE_JAVASCRIPT_PRETTIER=true -e FILTER_REGEX_EXCLUDE='.app/static/qrcode.min.js' -e RUN_LOCAL=true -v /home/jack/Repos/LinkShort:/tmp/lint ghcr.io/super-linter/super-linter:slim-v8.6.0\n```\n\nIf Super Linter reports any ERRORS (not warnings), you MUST fix all errors in the relevant files before allowing the commit. Do not proceed with the commit until Super Linter reports \"All files and directories linted successfully\". If the command is not a git commit, allow it to proceed immediately without running Super Linter."
14+
"prompt": "A shell command is about to be executed. If it is a `git commit` command, you MUST first run Super Linter using this exact Docker command from the repo root before allowing the commit to proceed:\n\n```\ndocker run --rm -e DEFAULT_BRANCH=origin/main -e VALIDATE_ALL_CODEBASE=true -e VALIDATE_CSS=true -e VALIDATE_DOCKERFILE_HADOLINT=true -e VALIDATE_GITLEAKS=true -e VALIDATE_GITHUB_ACTIONS=true -e VALIDATE_HTML=true -e VALIDATE_MARKDOWN=true -e VALIDATE_PYTHON_BLACK=true -e VALIDATE_PYTHON_ISORT=true -e VALIDATE_YAML=true -e VALIDATE_JAVASCRIPT_PRETTIER=true -e FILTER_REGEX_EXCLUDE='.app/static/qrcode.min.js' -e RUN_LOCAL=true -v ${workspaceFolder}:/tmp/lint ghcr.io/super-linter/super-linter:slim-v8.6.0@sha256:a56c57c3fbe361bf07173c35c1a8bb3839fc64e363021fdb67798625ea3f3565\n```\n\nIf Super Linter reports any ERRORS (not warnings), you MUST fix all errors in the relevant files before allowing the commit. Do not proceed with the commit until Super Linter reports \"All files and directories linted successfully\". If the command is not a git commit, allow it to proceed immediately without running Super Linter."
1515
}
1616
}

Dockerfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ RUN set -e; \
2525
make install; \
2626
ln -s /usr/local/bin/python3.15 /usr/local/bin/python;
2727

28-
# Install python dependencies into a target directory
28+
# Install python dependencies into a target directory, then remove pip so it
29+
# is not present in the runtime image (avoids shipping pip CVEs at runtime)
2930
RUN set -e; \
3031
pip3.15 install --no-cache-dir --upgrade 'pip==26.0'; \
31-
PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 pip3.15 install --no-cache-dir -r requirements.txt --target /packages;
32+
PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 pip3.15 install --no-cache-dir -r requirements.txt --target /packages; \
33+
pip3.15 uninstall -y pip setuptools wheel 2>/dev/null || true; \
34+
find /usr/local/lib/python3.15 -type d -name 'pip*' -exec rm -rf {} + 2>/dev/null || true; \
35+
find /usr/local/lib/python3.15 -type d -name 'setuptools*' -exec rm -rf {} + 2>/dev/null || true;
3236

3337

3438
# Stage 2: Runtime Stage using scratch Image

readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,8 @@ for f in app/static/*; do echo "$(basename $f): sha384-$(openssl dgst -sha384 -b
145145
> In production, you must also purge the Cloudflare cache for any updated files, otherwise the CDN will continue serving the old version.
146146
> This can be done in the Cloudflare dashboard under Caching > Configuration > Custom Purge, entering the full URL of each updated file.
147147
148+
## AI Development
149+
150+
This project uses [Kiro](https://kiro.dev) as its AI-powered IDE — see [`.kiro/steering/project.md`](.kiro/steering/project.md) for the agent instructions and conventions that guide it.
151+
148152
## Developed by [Jack](https://jacksec.engineer)

0 commit comments

Comments
 (0)