Skip to content

Security Audit Report โ€” go-tool-base

Date: 2026-04-02
Auditor Role: Senior Security Analyst (Go specialist)
Scope: Full codebase review โ€” authentication, cryptography, input validation, supply chain, information disclosure
Branch: develop (commit 50e9cbc)
Status: Reviewed โ€” all findings triaged with owner responses


Executive Summary

The go-tool-base project demonstrates a strong security posture overall. TLS configuration is hardened, HTTP clients use secure defaults, cryptographic operations use crypto/rand, and error handling is structured. Several findings were identified and triaged with the project owner. Code fixes, specification drafts, CI tooling, and documentation have been produced as part of this audit.

Finding counts by severity and resolution:

Severity Count Spec Drafted Fixed Documented False Positive
HIGH 3 2 0 0 1
MEDIUM 8 2 2 3 1
LOW 6 0 1 4 1

Part 1: Findings and Resolutions

HIGH Severity

H-1: Unencrypted Credential Storage at Rest

Location: pkg/config/container.go:116-118, pkg/setup/ai/ai.go:327-366, pkg/setup/github/github.go:189

Description: API keys for AI providers (Claude, OpenAI, Gemini), GitHub tokens, and Bitbucket app passwords are stored as plaintext YAML/JSON in the user's config file on disk.

Evidence:

// pkg/config/container.go:116-118
func (c *Container) WriteConfigAs(filename string) error {
    return c.v.WriteConfigAs(filename)
}

Owner Response: Agreed this is a concern. In non-development environments, credential management is expected to be handled externally (CSI drivers, Kubernetes Secrets, Vault). The solution must be selective โ€” not all environments need OS keychain integration. The env-var approach already exists but isn't the default setup path.

Resolution: SPEC DRAFTED
Specification: docs/development/specs/2026-04-02-credential-storage-hardening.md
Key proposals: make env-var references the default in the setup wizard, optional OS keychain integration gated behind a build tag, documented trust model across deployment contexts.


H-2: Agent Tool Executes Arbitrary Commands

Location: internal/agent/tools.go:196

Description: Code at the cited location uses exec.CommandContext with a //nolint:gosec comment.

Owner Response: FALSE POSITIVE. There is no exec_command AI tool defined โ€” this was an intentional decision to prevent AI from running arbitrary commands. The cited code is a tool-building helper with the //nolint tag because it's a helper function that cannot determine the safety of the constructing call being made. It is not an AI-accessible tool.

Resolution: NO ACTION REQUIRED


H-3: No Checksum Verification for Downloaded Release Binaries

Location: pkg/setup/update.go (download flow), pkg/setup/checksum.go

Description: The remote update download path relies solely on HTTPS transport integrity. The local UpdateFromFile() path already has SHA-256 verification.

Owner Response: Agreed, but noted that checksums hosted at the same VCS provider don't protect against full platform compromise. Needs a full spec to address trust model considerations.

Resolution: SPEC DRAFTED
Specification: docs/development/specs/2026-04-02-remote-update-checksum-verification.md
Key proposals: download and verify checksums.txt alongside release assets, fail-open by default with strict mode opt-in, cosign keyless verification as a future phase for defense-in-depth.


MEDIUM Severity

M-1: Health Endpoints Expose Internal Error Messages

Location: pkg/http/server.go:26-39, pkg/controls/controls.go:170, pkg/controls/healthcheck.go:62

Description: Health endpoints return raw error messages from service health checks in the ServiceStatus.Error field.

Owner Response: The emitting of error messages is intentional. It is the developer's responsibility to ensure sensitive information does not leak through these endpoints.

Resolution: DOCUMENTED (Accepted Risk)
- Added "Security Considerations" section to docs/how-to/register-health-checks.md - Recorded in docs/development/security-decisions.md as accepted risk M-1


M-2: Stack Traces Logged at Non-Debug Levels

Location: pkg/setup/middleware_builtin.go:50-56, pkg/config/container.go:139,141,203

Description: Panic recovery logged full stack traces at ERROR level. Config failures logged stack traces at WARN level.

Owner Response: Agreed. These should leverage debug-level logging for stack trace output.

Resolution: FIXED
- pkg/setup/middleware_builtin.go: Panic details logged at ERROR (no stack), stack trace logged separately at DEBUG - pkg/config/container.go: Stack traces moved from WARN/ERROR to DEBUG level at all three locations (lines 139, 141, 203)


M-3: Config File Permissions Not Restrictive

Location: pkg/setup/ai/ai.go, pkg/setup/github/github.go, pkg/setup/init.go

Description: Config files containing credentials were written with default umask permissions (typically 0644).

Owner Response: Agreed. All config files should get 0600 permissions.

Resolution: FIXED
- pkg/setup/ai/ai.go: Added p.FS.Chmod(targetFile, 0o600) after WriteConfigAs - pkg/setup/github/github.go: Added p.FS.Chmod(targetFile, 0o600) after WriteConfigAs - pkg/setup/init.go: Added props.FS.Chmod(targetFile, 0o600) after WriteConfigAs (with warning on failure)


M-4: Sensitive Data Not Cleared from Memory

Location: pkg/vcs/repo/repo.go:302-315, pkg/vcs/bitbucket/release.go:85-86

Description: SSH passphrases and VCS credentials remain in Go string variables for the process lifetime.

Owner Response: Agreed this is a limitation of Go's GC runtime. Document as accepted risk.

Resolution: DOCUMENTED (Accepted Risk)
Recorded in docs/development/security-decisions.md as accepted risk M-4.


M-5: URL Opened via System Command Without Scheme Validation

Location: pkg/telemetry/deletion.go:108-121

Description: The openURL() function passes URLs to system commands without validating the scheme.

Owner Response: Agreed. Needs a spec.

Resolution: SPEC DRAFTED
Specification: docs/development/specs/2026-04-02-url-scheme-validation.md
Key proposal: new pkg/browser/ package with scheme allowlist (https, http, mailto), replacing both existing call sites.


M-6: update --from-file Reads Arbitrary Paths

Location: pkg/cmd/update/update.go:56-77

Description: The --from-file flag accepts arbitrary filesystem paths.

Owner Response: Arbitrary paths are by design. Should be documented.

Resolution: DOCUMENTED (Accepted Risk)
Recorded in docs/development/security-decisions.md as accepted risk M-6.


M-7: Template Data from CLI Flags Not Escaped

Location: internal/generator/skeleton.go:551-557

Description: text/template renders scaffolded files with user-provided data. Code locations intentionally unescaped; non-code locations (YAML values, README prose) are not escaped either.

Owner Response: Template data is intentionally not escaped for code locations. However, values passed into non-code locations should be reviewed and escaped where appropriate. Needs a spec.

Resolution: SPEC DRAFTED
Specification: docs/development/specs/2026-04-02-generator-template-escaping.md
Key proposals: context-specific escape functions (escapeYAML, escapeMarkdown, etc.), applied via pipe syntax only at non-code template locations.


M-8: Environment Variable Auto-Binding Could Cause Config Pollution

Location: pkg/config/container.go:25-26

Description: viper.AutomaticEnv() allows any matching environment variable to override config.

Owner Response: The binding is somewhat intentional, but adding a custom prefix is the right approach. The config package should allow consumers to specify a custom prefix (GTB uses GTB_, other tools define their own).

Resolution: SPEC DRAFTED
Specification: docs/development/specs/2026-04-02-config-env-prefix.md
Key proposals: WithEnvPrefix option leveraging Viper's SetEnvPrefix(), backward-compatible (no prefix = current behavior), generator scaffolds new tools with a derived prefix.


LOW Severity

L-1: Debug-Mode Stack Traces in Error Handler

Location: pkg/errorhandling/handling.go:119-121,134

Owner Response: Intentional for development. Document accordingly.

Resolution: DOCUMENTED
Recorded in docs/development/security-decisions.md.


L-2: Provider Environment Variable Value Logged at Debug Level

Location: pkg/chat/client.go

Owner Response: Acceptable for debugging. Document accordingly.

Resolution: DOCUMENTED
Recorded in docs/development/security-decisions.md.


L-3: No Security Headers in Default HTTP Server

Location: pkg/http/server.go

Owner Response: Intentional. Document and provide how-to guides.

Resolution: DOCUMENTED
Created docs/how-to/security-headers.md with middleware examples covering HSTS, CSP, X-Content-Type-Options, and X-Frame-Options.


L-4: SSH Key Discovery Scans Entire ~/.ssh

Location: pkg/setup/github/ssh.go:132-168

Owner Response: Required for TUI initialisation. Only reads filenames, not contents.

Resolution: DOCUMENTED (Accepted Risk)
Recorded in docs/development/security-decisions.md.


L-5: Machine ID in Telemetry Deletion Emails

Location: pkg/telemetry/deletion.go:97-98, pkg/telemetry/machine.go:17-35

Owner Response: Should already be hashed. Verify.

Resolution: FALSE POSITIVE
Verified: HashedMachineID() in pkg/telemetry/machine.go collects OS machine ID, MAC address, hostname, and username, joins them, SHA-256 hashes the result, and returns only the first 8 bytes as 16 hex characters. Raw identifiable values are never transmitted.


L-6: fmt.Printf for Server URL Instead of Logger

Location: pkg/docs/serve.go:34

Owner Response: Needs to be addressed to use info logging.

Resolution: FIXED
Changed fmt.Printf("Documentation server starting at %s\n", url) to slog.Info("Documentation server starting", "url", url) using Go's standard library structured logger (no API signature change required).


Informational (Positive Findings)

These security controls are well-implemented and require no action:

Control Location Notes
TLS 1.2+ enforced, AEAD ciphers only pkg/http/tls.go:12-28 X25519 preferred, no weak suites
No InsecureSkipVerify anywhere Codebase-wide Verified via grep
HTTPSโ†’HTTP redirect downgrade blocked pkg/http/client.go:139 Explicit check in redirect policy
crypto/rand used consistently pkg/http/retry.go, pkg/chat/filestore.go No math/rand in security paths
SHA-256 checksums (local updates) pkg/setup/checksum.go Proper verification logic
HTTP request logging excludes headers/body pkg/http/client_middleware.go:60 Prevents credential leakage
Ed25519 SSH keys, 12-char min passphrase pkg/setup/github/ssh.go:29 Modern key type, good defaults
OAuth device flow (no local client secret) pkg/vcs/github/login.go Secure auth pattern
Sensitive config value masking in CLI output pkg/cmd/config/sensitive.go Pattern-based, shows last 4 chars only
Hardened HTTP client defaults pkg/http/client.go:22-30 Proper timeouts, connection limits
FIPS-mode builds .goreleaser.yaml GOLANG_FIPS=1
macOS binary notarization .goreleaser.yaml Apple notarization configured
Pre-commit detect-private-key hook .pre-commit-config.yaml Catches accidental secret commits
gosec linter enabled .golangci.yaml Catches common Go security issues

Part 2: CI Security Tooling

Previously In Place

Tool Where What it covers
golangci-lint (with gosec) CI + pre-commit Static analysis, common Go vulns
go test -race CI Data race detection
govulncheck Local only (just vuln) Known CVEs in dependencies
detect-private-key Pre-commit Accidental secret commits
macOS notarization Release pipeline Binary signing

Added in This Audit

Tool Workflow / Location What it covers
govulncheck .github/workflows/security.yaml + just vuln Known CVEs in Go dependencies (now in CI)
GitHub CodeQL .github/workflows/codeql.yaml SAST โ€” deep semantic analysis for Go
Trivy .github/workflows/security.yaml + just trivy Dependency & license vulnerability scanning
gitleaks .github/workflows/security.yaml + just gitleaks Secret scanning across git history
OSV Scanner .github/workflows/security.yaml + just osv-scan Google's vulnerability database for Go modules
OpenSSF Scorecard .github/workflows/scorecard.yaml Repository security health assessment
SBOM generation .goreleaser.yaml (sboms section) Software Bill of Materials for releases
Aggregate scan just security Runs all local security scans in sequence
Security policy SECURITY.md Vulnerability reporting process

Remaining Recommendations (Not Yet Implemented)

Tool Priority Notes
SLSA Provenance P3 Cryptographic build attestation โ€” consider when pipeline matures
GitHub Actions SHA pinning P3 Currently pinned to major versions; SHA pinning + Renovate for supply chain hardening

Part 3: Action Items

Immediate (Code fixes applied in this audit)

# Item Status Files Changed
1 M-2: Move stack traces to DEBUG level DONE pkg/setup/middleware_builtin.go, pkg/config/container.go
2 M-3: Restrict config file permissions to 0600 DONE pkg/setup/ai/ai.go, pkg/setup/github/github.go, pkg/setup/init.go
3 L-6: Replace fmt.Printf with slog.Info DONE pkg/docs/serve.go
4 CI: Security scanning workflows DONE .github/workflows/security.yaml, codeql.yaml, scorecard.yaml
5 CI: SBOM generation in releases DONE .goreleaser.yaml
6 CI: Local security scan recipes DONE justfile (trivy, gitleaks, osv-scan, security)
7 Security policy DONE SECURITY.md
8 Documentation: Accepted risks DONE docs/development/security-decisions.md
9 Documentation: Security headers how-to DONE docs/how-to/security-headers.md
10 Documentation: Health check security DONE docs/how-to/register-health-checks.md

Specs Requiring Review and Approval

# Spec File Key Decision Points
11 H-1: Credential storage hardening docs/development/specs/2026-04-02-credential-storage-hardening.md Keychain build tag vs runtime detection; default env var naming; GitHub OAuth flow interaction
12 H-3: Remote update checksum verification docs/development/specs/2026-04-02-remote-update-checksum-verification.md Strict mode default; cosign vs GPG for Phase 2; Bitbucket handling
13 M-5: URL scheme validation docs/development/specs/2026-04-02-url-scheme-validation.md Whether AllowedSchemes should be configurable; wrap vs replace cli/browser
14 M-7: Generator template escaping docs/development/specs/2026-04-02-generator-template-escaping.md Input validation for Name/Host/Org fields; escape function granularity
15 M-8: Config env var prefix docs/development/specs/2026-04-02-config-env-prefix.md Breaking vs non-breaking API approach; prefix validation; default for new tools

Follow-up Tasks (Post Spec Approval)

# Task Depends On Estimated Scope
16 Implement credential storage hardening Phase 1 (env-var default in wizard) Spec #11 approved Small โ€” setup wizard changes only
17 Implement credential storage hardening Phase 2 (optional keychain) Spec #11 Phase 1 Medium โ€” new dependency, build tags
18 Implement remote update checksum verification Spec #12 approved Medium โ€” touches all VCS providers
19 Implement URL scheme validation (pkg/browser/) Spec #13 approved Small โ€” new package, two call sites
20 Implement generator template escaping Spec #14 approved Medium โ€” template function map, audit all templates
21 Implement config env var prefix Spec #15 approved Medium โ€” config package change, generator update
22 Review and update SECURITY.md contact email โ€” Trivial โ€” replace placeholder
23 Evaluate SLSA provenance for release pipeline โ€” Research โ€” no spec needed
24 Evaluate SHA pinning for GitHub Actions โ€” Small โ€” Renovate/Dependabot config
25 Run /gtb-verify on all code changes from this audit โ€” Pre-commit gate

Report generated 2026-04-02. All line numbers reference the develop branch at commit 50e9cbc. Updated with owner responses and resolutions on the same date.