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
46 changes: 46 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(make help)",
"Bash(make deps)",
"Bash(make clean)",
"Bash(make test)",
"Bash(make test-all)",
"Bash(make test-integration)",
"Bash(make test-demo)",
"Bash(make fmt)",
"Bash(make fmt-check)",
"Bash(make lint)",
"Bash(make release-snapshot)",
"Bash(go build:*)",
"Bash(go test:*)",
"Bash(go vet:*)",
"Bash(go run ./cmd/example:*)",
"Bash(go run ./cmd/wraith:*)",
"Bash(go doc:*)",
"Bash(go list:*)",
"Bash(go mod tidy)",
"Bash(go mod download)",
"Bash(gofmt:*)",
"Bash(golangci-lint run:*)",
"Bash(osv-scanner --version)",
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git show:*)",
"Bash(git branch:*)",
"Bash(gh pr view:*)",
"Bash(gh pr diff:*)",
"Bash(gh pr list:*)",
"Bash(gh run view:*)",
"Bash(gh run list:*)"
],
"deny": [
"Bash(goreleaser release:*)",
"Bash(git push --force:*)",
"Read(./.env)",
"Read(./.env.*)"
]
}
}
2 changes: 2 additions & 0 deletions .github/workflows/lint.go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ jobs:
with:
go-version-file: go.mod
cache: true
- name: Check gofmt formatting
run: make fmt-check
- name: Run Go golangci-lint
run: make lint
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
!LICENSE
!NOTICE
!Makefile
!CLAUDE.md
!AGENTS.md
!.claude/settings.json

# ...even if they are in subdirectories
!*/
1 change: 1 addition & 0 deletions AGENTS.md
27 changes: 27 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Wraith

Dependency vulnerability scanner that wraps [osv-scanner](https://github.com/google/osv-scanner) behind a friendlier CLI and a Go library. Module path is `github.com/ghostsecurity/wraith`. No third-party Go dependencies, and it must stay that way, since a scanner that pulls in a large dependency tree undercuts its own point.

## Layout

- `pkg/wraith.go` mirrors the osv-scanner JSON schema as Go structs.
- `pkg/scanner.go` resolves and shells out to the osv-scanner binary, parses its JSON, and defines the functional `ScanOption` API.
- `pkg/convert.go` reduces the raw result into simplified findings, counts, and severity or license filters.
- `cmd/wraith/main.go` is the CLI with `scan`, `download-db`, and `version` subcommands, plus the text, JSON, and markdown renderers.
- `cmd/example/main.go` shows library usage.

## Binary resolution

`NewScanner` looks for osv-scanner in three places in order: an explicit path argument, a binary sitting next to the wraith executable, then `$PATH`. Releases ship the bundled binary alongside wraith, which is why the second case exists. The pinned version lives in three places that have to move together on a bump: `OSV_VERSION` in `scripts/download-osv-scanner.sh`, `.goreleaser.yaml`, and `.github/workflows/test.go.yml`.

## Working on it

Run `make test` for unit tests, which is what CI runs and which needs no osv-scanner binary. Anything covering the real scanner belongs behind `-short` so that stays true. `make test-integration` and `make test-demo` exercise the live binary and require osv-scanner on `$PATH`. Run `make lint` before opening a PR. Use `make release-snapshot` to check a goreleaser change without tagging.

Scanner behavior is verified against the fixtures in `pkg/testdata`, where `lockfiles/` holds real lockfiles and `output/` holds recorded osv-scanner JSON. Prefer extending those fixtures over mocking the binary.

## Conventions

Run `make fmt` after touching any Go file. CI runs `make fmt-check` on every pull request and fails the build when a file is not gofmt-formatted, so an unformatted edit blocks the merge rather than getting cleaned up later.

Commits and PR titles carry the Jira key, as in `GHO-11706: bump bundled osv-scanner to v2.3.8`. Errors wrap with `%w` and read as lowercase phrases. The CLI writes results to stdout and diagnostics to stderr, so keep that split intact for the agents and pipelines consuming this tool.
19 changes: 17 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,29 @@ test: ## Run unit tests only (fast, no OSV-Scanner required)

.PHONY: test-integration
test-integration: ## Run integration tests (requires OSV-Scanner)
@which osv-scanner || (echo "osv-scanner not found in PATH." && exit 1)
@which osv-scanner || (echo "osv-scanner not found in PATH." && exit 1)
go test -v ./pkg/... -run "Integration"

.PHONY: test-demo
test-demo: ## Run live demo with real lockfiles
@which osv-scanner || (echo "osv-scanner not found in PATH." && exit 1)
@which osv-scanner || (echo "osv-scanner not found in PATH." && exit 1)
go test -v ./pkg/... -run "QuickScan"

.PHONY: fmt
fmt: ## Format all Go files with gofmt
gofmt -w .

.PHONY: fmt-check
fmt-check: ## Fail if any Go file is not gofmt-formatted
@unformatted=$$(gofmt -l .); \
if [ -n "$$unformatted" ]; then \
echo "Not gofmt-formatted:"; \
echo "$$unformatted"; \
echo "Run 'make fmt' to fix."; \
exit 1; \
fi
@echo "All Go files are gofmt-formatted"

.PHONY: lint
lint: ## Run linter
@which golangci-lint || go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
Expand Down
Loading