Conversation
- go.mod initialised with module github.com/Pringled/agentguard (Go 1.26) - internal/models: Severity enum, Finding, ScanResult, RiskScore, RiskReport - internal/constants: K8SProdPatterns, HighRiskEnvKeys (45 keys, exact Python parity), RC sentinels - internal/scanner: Scanner interface + Base helper (RunCommand with timeout/notfound, ToolAvailable) - internal/cli: cobra root + scan subcommand stub (flags wired, mutual exclusion, fail-on validation) - cmd/agentguard/main.go: entry point - Makefile: build, test, lint, tidy, clean targets - Unit tests for models, constants, base scanner — all passing - .planning/ROADMAP.md and REQUIREMENTS.md added
- APIKeyScanner: detects secrets in env vars (HIGH) and credential files (MODERATE) Covers env vars (OpenAI, Anthropic, HuggingFace, Replicate, Mistral, Cohere, AWS, GCP, Slack, Discord, 45 HighRiskEnvKeys total), AWS credentials file, gcloud config dir, .npmrc, .pypirc, gh hosts.yml - LocalScanner: detects local infra access — kubectl context (MODERATE/CRITICAL), wildcard k8s permissions (CRITICAL), Docker daemon, SSH agent keys, Terraform .tf files, .env files, GPG secret keys, and 8 deploy CLIs (gh, vault, pulumi, heroku, fly, railway, vercel) - RC_TIMEOUT emits LOW uncertainty finding across all checks (not silence) - Both scanners never return skipped=true - Exported RunCommandFn, ToolAvailableFn, Cwd, HomeDir for test injection - 57 scanner tests + 20 passing from Phase 1 = 77 total, all green with -race
- AWSScanner: root→CRITICAL (no extra IAM call), user/role policy listing→HIGH for AdministratorAccess / PowerUserAccess / IAMFullAccess; skips on not-installed, timeout, not-configured, or JSON parse error; correctly extracts role name (not session name) from assumed-role ARNs - AzureScanner: Owner/Contributor→HIGH; object-ID server-side scoping catches group-inherited assignments; fallback path with LOW uncertainty finding when object-ID unavailable; client-side principalName filter in fallback to avoid false positives; missing-user-identity guard returns LOW and skips enumeration - GCPScanner: owner/editor/billing.admin/compute.admin/bigquery.admin/ iam.serviceAccountAdmin/resourcemanager.projectCreator→HIGH; candidate-member matching with and without 'type' field (avoids false negatives in CI); no false positives for other principals; LOW finding when no project set or IAM fetch fails - All three skip on not-installed and timeout; exported RunCommandFn for test injection - 101 scanner tests total (57 Phase 2 + 44 Phase 3), all green with -race
- internal/scoring/weights.go: FinancialWeights, MutationWeights, secret exposure constants, ScoreToLabel() - internal/scoring/scorer.go: RiskScorer.Compute() with financial, mutation, secretExposure sub-scorers; exact Python parity including seen-set deduplication and K8s prod-pattern detection - internal/orchestrator/orchestrator.go: ScanOrchestrator runs scanners concurrently via goroutines; panic recovery produces skipped ScanResult with 'internal error: ...' skip_reason - Tests: 38 new tests (159 total), all green with -race
- JSON, Markdown, Table formatters in internal/output/ - Table formatter uses tablewriter v1.1.3 + fatih/color for ANSI severity colors - CLI fully wired: all 5 scanners, orchestrator, all 3 formatters - --ci / --fail-on exit code logic - --json / --markdown mutual exclusion guard - 176 tests passing, all green with -race
- .goreleaser.yml: builds darwin/linux amd64+arm64, windows amd64; strips debug info via ldflags; produces checksums; pushes Homebrew formula to Pringled/homebrew-tap - .github/workflows/release.yml: triggers on v* tags, runs tests, then GoReleaser - .github/workflows/ci.yml: runs vet + race tests + build on every push/PR - Makefile: adds snapshot and release-dry-run targets; ldflags wire version/commit/date into binary - main.go + cli.go: BuildInfo struct propagates version/commit/date to 'agentguard version' subcommand - .gitignore: scope binary ignores to root (was accidentally ignoring cmd/agentguard/ directory)
HIGH fixes: - Rename module path to github.com/Pringled/agentguard-go to match repo remote - Skip ~-based credential file checks in APIKeyScanner when home dir is unavailable - Return errors from runScan() instead of os.Exit() inside cobra RunE (CI exit kept) - Use errors.As for type assertions in base.go (handles wrapped errors correctly) - Remove custom min() from local.go (shadows Go 1.21+ builtin) - Rename loop var 'f' -> 'finding' in json.go to avoid shadowing receiver MEDIUM fixes: - go mod tidy: promote fatih/color and tablewriter to direct dependencies - gofmt -w all files (models.go, json.go, azure_test.go, helpers.go, local_test.go, weights.go) - orchestrator: add Namer interface; scanners implement ScannerName() so panic fallbacks use canonical IDs (aws|azure|gcp|api_keys|local) instead of unstable %T strings - constants: standardize K8SProdPatterns and HighRiskEnvKeys to map[string]bool - Move strPtr() helper from aws.go to new helpers.go in scanners package - local_test.go: replace hand-rolled startsWith() with strings.HasPrefix() - local_test.go: remove dead 'cfg := baseMock() / _ = cfg' in NeverSkips test - cli: add cli_test.go covering version, --json+--markdown exclusion, --fail-on range
…lices, error printing fix
… to package scanners_test, expand README
…te output through cmd.OutOrStdout()
Merge internal/scanner, internal/scanners, internal/orchestrator, and
internal/constants into a single internal/scan package. RunAll() replaces
the orchestrator, sentinel codes and credential data live in scan.go.
- scoring.Compute() replaces RiskScorer{}.Compute() — zero-field struct removed
- Detail *string → Detail string (json:omitempty) across models and output
- Name() string added to Scanner interface; Namer interface removed
- weights.go merged into scorer.go; dead GAC explicit branch removed
- Full test suite created in internal/scan/ (testhelpers, scan, apikeys,
local, aws, azure, gcp tests)
- scorer_test.go updated; weights_test.go deleted
- models_test.go and formatters_test.go updated for string Detail field
- Delete IsK8SProdPattern() and the backing map; K8SProdPatterns is now a plain slice literal (scoring iterates it directly, map was unused) - Delete HighRiskEnvKeyCount(); test uses len(scan.HighRiskEnvKeys) directly - Replace sliceContainsStr helper with slices.Contains (stdlib, Go 1.21+) - Fix stale strPtr comment (Detail is already plain string)
…output Rewrites the terminal table formatter to match Python Rich's SIMPLE_HEAVY style: italic centred title, bold headers, full-width ━━━ rule, word-wrapped description column that adapts to terminal width, and a rounded ╭─╮ panel for the risk summary. Drops tablewriter, fatih/color and their clipperhouse transitive deps; adds charmbracelet/lipgloss (net dep count neutral).
- Rename cmd/agentguard/ → cmd/agentscan/ - Remove 'scan' subcommand; flags (--json, --markdown, --ci, --fail-on) now live on the root command so the UX is just 'agentscan [flags]' - Keep 'version' as the only subcommand - Update module path to github.com/Pringled/agentscan in go.mod and all .go files - Rewrite cli_test.go: TestScanCmd_* → TestRootCmd_*, drop 'scan' from SetArgs - All tests pass (go test ./...)
- Update all agentguard references to agentscan - Remove 'scan' subcommand from all examples (root cmd now does the scan) - Update GitHub URLs to github.com/Pringled/agentscan - Update Homebrew tap name - Clean up wording throughout
- .goreleaser.yml: project name, build id, binary, archive id, brew formula name, homepage URL - .gitignore: update ignored binary patterns - internal/models/models.go: update package and struct comments - internal/scan/scan_test.go: update sentinel binary name strings in tests
Separate timeout/unknown-risk findings from confirmed-but-limited (LOW) findings. UNCERTAIN means the check could not complete — unknown ≠ safe. - models: add SeverityUncertain = "UNCERTAIN" - local.go: timeoutFinding() emits UNCERTAIN - gcp.go: IAM policy fetch failure → UNCERTAIN - azure.go: identity-unknown, role-listing-failure, fallback-client-side → UNCERTAIN - table.go: UNCERTAIN renders in grey (color 8), no bold - scorer.go: UNCERTAIN findings contribute flat weight 1.0 to all three risk dimensions (financial, mutation, secret exposure) - tests: update assertTimeoutFinding and all UNCERTAIN-case assertions
Fix inaccurate severity levels (STRIPE was CRITICAL, should be HIGH) and update risk scores to match real demo output. Add vault and kubectl prod-cluster rows which are more illustrative of the tool's purpose.
Drop: ANTHROPIC_API_KEY (redundant with OPENAI), GITHUB_TOKEN, .aws/credentials + .netrc (ugly temp paths), kubectl:wildcard (redundant with context row), gh + ssh-agent. Keep: OPENAI + STRIPE (AI + payment), AdministratorAccess + Owner (AWS + Azure admin), GCP LOW (shows nuance), kubectl prod + vault CRITICAL, docker MODERATE. All four severity levels, all scanner types. Score block now reflects actual demo output.
- Add HF_TOKEN to highRiskEnvKeys (HuggingFace canonical env name used by huggingface-hub; HUGGINGFACE_TOKEN was already present but HF_TOKEN is the one the library actually reads) - Add HF_TOKEN financial weight (2) in scorer.go, matching HUGGINGFACE_TOKEN - Update count guard: 45 -> 46 - scorer.go: drop redundant second sentence from package doc - output/json.go: remove hollow type-restating doc comments on private structs - apikeys_test.go: replace hand-rolled string search with strings.Contains; move containsStr helper to testhelpers_test.go (shared by all scanner tests)
Setup: - .golangci.yml: errcheck, govet, staticcheck, ineffassign, misspell, gosec, cyclop (max 15); gofmt + goimports formatters; test files excluded from gosec - .pre-commit-config.yaml: pre-commit-hooks (whitespace/yaml/json/ merge-conflict/large-files) + go-mod-tidy + golangci-lint --fix - Makefile: lint target → golangci-lint; add lint-fix, hooks targets - ci.yml: replace go vet with golangci-lint-action@v7 Code fixes (all surfaced by golangci-lint, zero pre-existing suppressions): - staticcheck QF1008: s.Base.RunCommand → s.RunCommand in aws/azure/ gcp/local.go (Base is embedded; explicit field selector was redundant) - errcheck: output/table.go — introduce errWriter helper that absorbs the first write error; replaces 7 unchecked fmt.Fprintln/Fprintf calls - errcheck: cli.go version cmd + markdown.go Render — single-line //nolint:errcheck with justification (stdout write failure unrecoverable) - gosec G115: //nolint on uintptr→int fd cast in terminalWidth (safe; fd is always a small non-negative integer)
.pre-commit-config.yaml: drop comments entirely (repo URLs are self-explanatory) Makefile help: remove em-dash separators, plain descriptions only .golangci.yml: reword gosec exclusion comment to a plain statement README.md: replace all em-dash definition patterns (**Term** - desc) with colon style or plain sentences; remove em-dashes from table cells and inline prose
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.
No description provided.