Security review / vulnerability hunting in existing Go code (passive “notice issues while working” and active “scan the repo and report findings”).
It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).
MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, JWTs, database URLs with credentials, signing keys, client secrets).
MUST NOT “fix” security by disabling protections (e.g., InsecureSkipVerify, GOSUMDB=off for public modules, wildcard CORS + credentials, removing auth checks, disabling CSRF defenses on cookie-auth apps).
MUST provide evidence-based findings during audits: cite file paths, code snippets, build/deploy configs, and concrete values that justify the claim.
MUST treat uncertainty honestly: if a control might exist in infrastructure (reverse proxy, WAF, service mesh, platform config), report it as “not visible in app code; verify at runtime/config.”
MUST keep fixes minimal, correct, and production-safe; avoid introducing breaking changes without warning (especially around auth/session flows, and proxies).
Path parameters from routers (including values extracted from URL paths)
JSON/XML/YAML bodies, multipart form parts, uploaded files
Any data from external systems (webhooks, third-party APIs, message queues)
Any persisted user content (DB rows) that originated from users
Configuration values that might be attacker-influenced in some deployments (headers set by upstream proxies, environment variables in multi-tenant systems)
A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.
MUST run a supported Go major version and keep to the latest patch releases.
MUST treat Go standard library patch releases as security-relevant (many security fixes land in stdlib components like net/http, crypto/*, parsing packages).
MUST use Go modules with committed go.mod and go.sum.
MUST NOT disable module authenticity mechanisms for public modules (checksum DB) unless you have a controlled, documented replacement.
MUST run govulncheck (source scan and/or binary scan) in CI and address findings.
NOTE: Upgrading dependencies and the core Go version can break projects in unexpected ways. Focus on only security-critical dependencies and if noticed, let the user know rather than upgrading automatically.
Required:
MUST run a supported Go major release and apply patch releases promptly.
SHOULD treat patch releases as security-relevant, even if your application code didn’t change.
Insecure patterns:
Production builds pinned to old Go versions without a patching process.
Docker images like golang:1.xx or custom base images that are not updated regularly.
CI pipelines that intentionally suppress Go updates.
Detection hints:
Inspect CI (.github/workflows, gitlab-ci.yml, etc.) for go-version: or toolchain setup.
Inspect Dockerfiles for FROM golang: tags.
Inspect go.modgo directive and any toolchain pinning.
Fix:
Upgrade to the latest patch of a supported Go version.
Add an automated check (CI) that fails when Go is below an approved minimum.
Notes:
Go publishes regular minor releases that frequently include security fixes across standard library packages.
Severity: Medium (DoS risk; can be High for upload-heavy apps)
Required:
MUST enforce a global maximum request body size for endpoints that accept bodies.
MUST enforce strict multipart upload limits and avoid unbounded form parsing.
SHOULD enforce per-route limits when some endpoints legitimately need larger bodies.
SHOULD set upstream (proxy) limits as defense-in-depth.
Insecure patterns:
Reading r.Body with io.ReadAll(r.Body) without a size cap.
Calling r.ParseMultipartForm(...) with overly large limits (or forgetting size controls).
Accepting file uploads with no limits on file size, number of parts, or total body size.
Detection hints:
Search for io.ReadAll(r.Body), json.NewDecoder(r.Body), ParseMultipartForm, FormFile, multipart.
Look for missing http.MaxBytesReader or equivalent per-handler limiting.
Look for “upload” endpoints and check limits.
Fix:
Wrap request bodies with http.MaxBytesReader(w, r.Body, maxBytes) before parsing.
For multipart, set conservative limits and validate file sizes/part counts explicitly.
Set proxy limits (e.g., at ingress) in addition to app limits.
Notes:
There are known vulnerability classes and advisories related to excessive resource consumption in multipart/form parsing; treat unbounded parsing as a security issue.
NOTE: This only applies to production configurations. These endpoints are often used for debug or dev endpoints. If found, confirm that it would be reachable from the actual production deployment.
Required:
MUST NOT expose net/http/pprof handlers on a public internet-facing listener without strong access controls.
SHOULD run diagnostics on a separate, internal-only listener (loopback/VPC-only) and require auth.
MUST review what diagnostic endpoints reveal (stack traces, memory, command lines, environment, internal URLs).
Insecure patterns:
Side-effect import import _ "net/http/pprof" in a server binary with a public mux.
/debug/pprof/* reachable without auth.
/debug/vars (expvar) reachable without auth.
Detection hints:
Search for net/http/pprof imports (including blank imports).
Search for route prefixes /debug/pprof, /debug/vars.
Check whether http.DefaultServeMux is used and whether any debug handlers register globally.
Fix:
Remove diagnostics from production builds, or bind them to an internal-only listener.
Content-Security-Policy (CSP) appropriate to the app. NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.
MUST set Secure on cookies that carry auth/session state. IMPORTANT NOTE: Only set Secure in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.
MUST set HttpOnly on auth/session cookies.
SHOULD set SameSite=Lax by default (or Strict if compatible), and only use None when necessary (and only with Secure).
SHOULD set bounded lifetimes (Max-Age/Expires) appropriate to the app.
Insecure patterns:
Setting auth/session cookies without Secure in HTTPS deployments.
Cookies without HttpOnly for session identifiers.
SameSite=None for cookie-authenticated apps without a strong CSRF strategy.
Detection hints:
Search for http.SetCookie, &http.Cookie{, Set-Cookie.
Inspect cookie flags in auth/session code.
Fix:
Set the correct fields on http.Cookie and centralize cookie creation.
Notes:
SameSite is defense-in-depth and does not replace CSRF protections for cookie-auth apps.
IMPORTANT NOTE: If cookies are not used for auth (e.g., pure bearer token in Authorization header with no ambient cookies), CSRF is not a risk for those endpoints.
Required:
MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
SHOULD use a well-tested CSRF library/middleware rather than rolling your own.
MAY use additional defenses (Origin/Referer checks, Fetch Metadata, SameSite cookies), but tokens remain the primary defense for cookie-authenticated apps.
If tokens are impractical, or for small applications:
MUST at a minimum require a custom header to be set and set the session cookie SESSION_COOKIE_SAMESITE=lax, as this is the strongest method besides requiring a form token, and may be much easier to implement.
Insecure patterns:
Cookie-authenticated JSON endpoints that mutate state with no CSRF checks.
Using GET for state-changing actions.
Detection hints:
Enumerate all non-GET routes and identify auth mechanism.
Look for CSRF middleware usage; if absent, treat as suspicious in browser-facing apps.
Fix:
Add CSRF middleware and ensure it covers all state-changing routes.
If the service is an API intended for non-browser clients, avoid cookie auth; use Authorization headers.
Note: For small stand alone projects this is less important. It is most important when deploying into an LAN or with other services listening on the same server.
Required:
MUST treat outbound requests to user-provided URLs as high risk.
SHOULD allowlist hosts/domains for any user-influenced URL fetch.
SHOULD block access to localhost/private IP ranges/link-local addresses and cloud metadata endpoints.
MUST restrict schemes to http/https (no file:, gopher:, etc.).
MUST set client timeouts and restrict redirects.
Insecure patterns:
http.Get(r.URL.Query().Get("url"))
“URL preview” / “webhook test” endpoints that fetch arbitrary URLs.
Detection hints:
Search for http.Get, client.Do, and URL values derived from requests/DB.
Identify features that fetch remote resources.
Fix:
Parse URLs strictly; enforce scheme and allowlisted hostnames.
Resolve DNS and enforce IP-range restrictions (with care for DNS rebinding).
Set timeouts, disable redirects unless needed, and cap response sizes.