Projects v2 board + publishable docker image + rename secscan→security-scan#6
Merged
Conversation
…ctory
Two changes that go together:
1. Config files moved into a single config/ directory so the host-side bind
mount is one volume instead of a per-file mount. This makes packaging the
scanner as a Claude Code skill clean — every per-deployment file (main
config, 1Password env file) lives in one place that maps to /config inside
the container.
Moved (git tracked, history preserved via rename):
config.example.yaml -> config/config.example.yaml
.env.1password.tpl.example -> config/.env.1password.tpl.example
And (gitignored, plain move):
config.yaml -> config/config.yaml
.env.1password.tpl -> config/.env.1password.tpl
.gitignore updated. secscan.sh now defaults to config/ as the config
directory, accepts --config-dir as a new flag, mounts the directory at
/config:ro inside the container, and resolves env_file relative to that
dir. SECSCAN_CONFIG_DIR env var added for skill use ("scan project foo
uses this config dir, project bar uses that one").
Dockerfile unchanged — it already expected /config/config.yaml.
2. New `skill/` directory ships a Claude Code skill bundle:
skill/SKILL.md agent instructions (frontmatter + body)
skill/references/README.md high-level reference
skill/references/CONFIG.md full config schema + 1Password walkthrough
skill/references/RUN.md runbook, flags, exit codes, recovery
skill/install.sh copies skill into ~/.claude/skills/secscan/
The skill is small — it's just instructions pointing at $SECSCAN_HOME for
the actual scanner. Users clone this repo once, export SECSCAN_HOME, run
skill/install.sh once. Updates to the scanner don't require re-installing
the skill.
The skill's SKILL.md documents the hard rules (never --no-dry-run without
confirmation, never expose secrets, project board is source of truth).
README updated with a "Claude Code skill" section and all config paths
adjusted. 221 tests pass; `./secscan.sh check` reports green against the
new layout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR reorganizes secscan’s per-deployment configuration into a dedicated config/ directory (so the wrapper bind-mounts a single volume) and adds a Claude Code skill bundle (skill/) that documents how an agent should install and operate secscan safely.
Changes:
- Move config/templates under
config/and update docs +.gitignoreaccordingly. - Update
secscan.shto support--config-dir/SECSCAN_CONFIG_DIRand mount the whole config directory at/config. - Add
skill/bundle (SKILL.md + references + installer script) and document it in the root README.
Reviewed changes
Copilot reviewed 7 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
secscan.sh |
Adds config-directory semantics, new flag/env override, and directory bind-mounting. |
README.md |
Updates setup instructions for config/ layout and documents the Claude Code skill. |
.gitignore |
Updates ignored paths for the new config layout. |
config/config.example.yaml |
Example config template under the new directory structure. |
config/.env.1password.tpl.example |
1Password env-file template under the new directory structure. |
skill/SKILL.md |
Agent-facing instructions: triggers, run procedure, and safety rules. |
skill/references/README.md |
High-level reference for what secscan is and how it behaves. |
skill/references/CONFIG.md |
Config schema and secrets setup reference for the agent. |
skill/references/RUN.md |
Runbook: flags, exit codes, troubleshooting, and reporting guidance. |
skill/install.sh |
Installer to copy the skill bundle into the user’s Claude skills directory. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
215
to
217
| local config_dir="${SECSCAN_CONFIG_DIR:-$DEFAULT_CONFIG_DIR}" | ||
| local config="${SECSCAN_CONFIG:-$config_dir/config.yaml}" | ||
| local extra_args=() |
Comment on lines
126
to
128
| local config_dir="${SECSCAN_CONFIG_DIR:-$DEFAULT_CONFIG_DIR}" | ||
| local config="${SECSCAN_CONFIG:-$config_dir/config.yaml}" | ||
| local ok=1 |
| 2. **Check config.** Run `$SECSCAN_HOME/secscan.sh check`. If it reports any | ||
| `✗`, walk the user through the fix using `references/CONFIG.md` as the | ||
| source of truth. Common failure modes: | ||
| - missing `config/config.yaml` → copy from `config.example.yaml` |
Comment on lines
327
to
330
| exec docker run --rm \ | ||
| -v "$config":/config/config.yaml:ro \ | ||
| -v "$config_dir":/config:ro \ | ||
| "${env_args[@]}" \ | ||
| "$IMAGE" "${extra_args[@]+"${extra_args[@]}"}" |
Comment on lines
366
to
369
| exec op run --env-file="$ef" -- docker run --rm \ | ||
| -v "$config":/config/config.yaml:ro \ | ||
| -v "$config_dir":/config:ro \ | ||
| "${env_args[@]}" \ | ||
| "$IMAGE" "${extra_args[@]+"${extra_args[@]}"}" |
Shifts the skill packaging strategy: instead of bundling skill files in this
repo, this repo produces a published Docker image (`leverj/security-scan`)
that ships a SECSCAN-MANIFEST.yaml inside. The companion ai-skills repo
hosts the actual Claude Code skill, which drives the image and reads the
manifest to do informed upgrades.
What's in this commit:
- Drop `skill/` directory entirely — that was a wrong-repo prototype. The
real skill lives in leverj/ai-skills.
- `SECSCAN-MANIFEST.yaml` at repo root (baked into image at
/app/SECSCAN-MANIFEST.yaml). A consumer skill reads it with:
docker run --rm --entrypoint cat \\
leverj/security-scan:<tag> /app/SECSCAN-MANIFEST.yaml
It declares the image version, config_schema_version, changelog,
breaking_changes, and most importantly a typed list of:
- config.new_fields (skill ADDs these to user's config.yaml if absent)
- config.renamed_fields (skill renames in-place)
- config.removed_fields (skill strips with user confirmation)
This is the contract that lets the consumer skill do "image updated;
here's what changes; want to upgrade?" without baking version-specific
migration code into the skill itself.
- `.github/workflows/publish.yml` — on a `v*` tag, builds amd64+arm64
images and pushes leverj/security-scan:<tag> + :latest to Docker Hub.
A guard step verifies pyproject.toml's version + SECSCAN-MANIFEST.yaml's
version match the tag before publishing. Requires repo secrets
DOCKERHUB_USERNAME + DOCKERHUB_TOKEN.
- Bump pyproject.toml version 0.1.0 -> 0.2.0 (matches manifest).
- README: replace the (now-removed) "Claude Code skill" section with a
short pointer to leverj/ai-skills.
Local smoke test: docker build + `--entrypoint cat /app/SECSCAN-MANIFEST.yaml`
returns the manifest verbatim. 221 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cosmetic rename across the codebase, image, manifest, and config: Python module secscan/ -> security_scan/ (underscore — Python identifier) PyPI name secscan -> security-scan CLI entry point secscan -> security-scan Wrapper script secscan.sh -> security-scan.sh Manifest filename SECSCAN-… -> SECURITY-SCAN-MANIFEST.yaml Spec filename secscan-spec -> security-scan-spec.md Env vars SECSCAN_* -> SECURITY_SCAN_* All log/user-facing strings now read `security-scan:` (hyphen). Python imports use `security_scan` (underscore) since identifiers can't contain hyphens. Fingerprint marker compatibility: new issues are filed with the `security-scan:` marker; the parser also accepts legacy `secscan:` markers so dedup against existing project items continues to work without a one-time backfill. Image name was already `leverj/security-scan` so no change there. Validated: - 221 tests pass after marker regex update - `pip install -e .` produces the `security-scan` CLI - `./security-scan.sh check` reports green - `docker build` succeeds and manifest extraction returns the new file Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The rename sweep over-applied the hyphen form to ci.yml; the lint step was pointed at security-scan/ which doesn't exist. The Python package directory is security_scan/ (Python identifiers don't allow hyphens). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the loose ends from the rename + Projects v2 work:
- security-scan.sh: SECSCAN_CONFIG_DIR -> SECURITY_SCAN_CONFIG_DIR.
The earlier rename regex required a word boundary and underscores are
word characters, so compound env-var names (like SECSCAN_CONFIG_DIR)
slipped through. Functional bug: users following the new naming
convention would set SECURITY_SCAN_CONFIG_DIR and find it ignored.
- Update stale docstrings/comments that still described the parent-epic /
sub-issue model:
security_scan/__init__.py module docstring + version bump 0.1.0 -> 0.2.0
security_scan/sync.py _labels_for docstring
security_scan/notify.py _default_digest docstring
security_scan/runners/syft.py module docstring
- Remove obsolete tools/backfill_markers.py. Its premise (backfill
fingerprint markers onto sub-issues filed by an even older tool) no
longer applies — Projects v2 items carry markers directly, and the
manifest-driven config migration is the path for future evolution.
- Rewrite security-scan-spec.md as v2:
* Drop parent-epic / sub-issue model throughout in favor of the
Projects v2 board.
* Add the new scanners (trivy, trufflehog, syft, codex, gemma).
* Add the cross-validation section.
* Add the image-manifest contract section.
* Add a build/release section documenting the Docker Hub publish flow.
* v1 lineage preserved in §17.
- Untrack .claude/scheduled_tasks.lock and add .claude/ to .gitignore;
it's per-checkout session state, not part of the repo.
Validated: 221 tests pass, ./security-scan.sh check is green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three related changes that together make the scanner ready to be installed as a skill from
leverj/ai-skills:Severity+Categorysingle-select fields. Removes the 100-sub-issue cap that was blocking real runs. (Originally landed in PR Projects v2 board + Codex/Gemma SAST with cross-validation #5 /bd606f7.)config/directory (clean one-volume mount), build + publish the scanner asleverj/security-scanon Docker Hub, and bake aSECURITY-SCAN-MANIFEST.yamlinto the image so consumer skills can do user-confirmed version upgrades with automated config migration.secscan→security-scan— cosmetic but pervasive.security-scanfor user-facing names (CLI, project, image),security_scanfor the Python module since identifiers can't have hyphens.Branch contents (5 commits)
18b66371f50d07SECURITY-SCAN-MANIFEST.yaml, Docker Hub publish workflow, pyproject 0.2.0.ea8de06secscan→security-scan/security_scan. Marker regex accepts legacysecscan:markers for backward compat on existing project items.345e902security_scan/, not the hyphenated form).215b0e2SECSCAN_CONFIG_DIRenv-var leak fixed; stale "sub-issue" docstrings rewritten; obsoletetools/backfill_markers.pyremoved;security-scan-spec.mdrewritten as v2 for the new architecture;.claude/added to.gitignore.Test plan
./security-scan.sh checkgreen against the newconfig/layout + renamed env vars.docker build .succeeds anddocker run --rm --entrypoint cat secscan:test /app/SECURITY-SCAN-MANIFEST.yamlreturns the manifest verbatim.DOCKERHUB_USERNAME+DOCKERHUB_TOKENrepo secrets, thengit tag v0.2.0 && git push origin v0.2.0to publish the first image to Docker Hub.Companion work
The Claude Code skill that drives the image is on
leverj/ai-skillsbranchfeat/security-scan-skill. It pulls + runsleverj/security-scan:<pinned-tag>, checks Docker Hub for newer tags, and applies the manifest's declared config migrations on user confirmation.🤖 Generated with Claude Code