taint: skip context.Context arguments during taint propagation to fix false positives#1543
Merged
ccojocar merged 3 commits intosecurego:masterfrom Feb 24, 2026
Merged
taint: skip context.Context arguments during taint propagation to fix false positives#1543ccojocar merged 3 commits intosecurego:masterfrom
context.Context arguments during taint propagation to fix false positives#1543ccojocar merged 3 commits intosecurego:masterfrom
Conversation
`context.Context` is a control-flow mechanism (deadlines, cancellation, request-scoped metadata) that does not carry user-controlled data relevant to taint sinks. When `request.Context()` is passed to downstream functions (gRPC clients, DB calls), the taint engine was conservatively marking all return values as tainted, causing cascading false positives for G703-G706 in any HTTP handler using the standard Go context pattern. Add `isContextType()` helper and skip `context.Context-typed` arguments at all four argument-scanning sites in the taint engine: interface method calls, external static method calls, external plain function calls, and `doTaintedArgsFlowToReturn` interprocedural analysis. Receiver taint propagation (e.g., `req.URL.Query().Get()`) is unaffected. ref: securego#1542
…ve prevention Add unit tests for the `isContextType` helper verifying it correctly identifies context.Context and rejects non-context types (http.Request, string, wrong package, pointer-wrapped, nil). Add G705 integration test exercising the HTTP handler + r.Context() pattern that previously caused false positives.
The isContextType function now properly unwraps pointer layers to detect context.Context types even when wrapped in pointers (e.g., *context.Context). This prevents false taint propagation to function outputs from context arguments. Added comprehensive test coverage for both direct context.Context and pointer variants to ensure robust type checking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1543 +/- ##
=======================================
Coverage 80.48% 80.48%
=======================================
Files 104 104
Lines 9621 9640 +19
=======================================
+ Hits 7743 7759 +16
- Misses 1402 1404 +2
- Partials 476 477 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ccojocar
approved these changes
Feb 24, 2026
|
@ccojocar , any chance we can get a release with this fix? |
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
context.Contextpropagating taint from*http.Requestthrough to unrelated function return valuesisContextType()helper and apply it at all four argument-scanning sites in the taint engineisContextTypeto unwrap pointer layers (e.g.,*context.Context) before the named-type checkisContextTypecovering positive, pointer, and negative casesr.Context()→ service call →w.Writepattern produces no false positiveProblem
The taint engine currently propagates taint through
context.Contextarguments.In idiomatic Go HTTP handlers, this creates a cascading false positive chain that
renders the taint-based rules (G703–G706) unusable on real-world codebases.
Consider this common pattern:
The taint chain works as follows:
r *http.Request— tainted (type source)r.Context()— tainted (method call on tainted receiver returns tainted value)s.grpcService.DoSomething(r.Context(), ...)— entire return tainted becauser.Context()is a tainted argument, and for interface/external calls the engine conservatively assumes any tainted argument taints the returnerrfrom the gRPC call — tainted (extracted from tainted call)status.FromError(err)→st.Message()— tainted via receiver chainw.Write(...)— sink reached with tainted data → G705 reportedIn a real codebase with this pattern, this produces 137 false positives across 14 files — every single gRPC call site in every HTTP handler, with zero true positives. This is consistent with the category of false positives reported in #1500.
Why
context.Contextshould not propagate taintcontext.Contextis a control-flow mechanism, not a data-carrying type relevant to taint sinks:ctxas the first argument to virtually every function that does I/O — treating it as a taint vector means every function that accepts a context and returns data gets its return value tainted, which defeats the purpose of taint analysisEven
context.WithValuestores metadata (trace IDs, auth info) that is not user-controlled input. A theoretical scenario where user input is stored in a context value and later written unsanitized to a response is exotic enough that it should not penalize the overwhelmingly common case.Fix
isContextTypehelper (taint/taint.go)Add an
isContextType(types.Type) boolhelper that:*context.Contextas well ascontext.Context)"context") and name ("Context")Applied at all four taint-propagation sites in
isTainted/doTaintedArgsFlowToReturn:val.Call.IsInvoke()) — e.g., gRPC service interface calls likes.service.Method(ctx, req)status.FromError(err)where the function body isn't availabledoTaintedArgsFlowToReturn— the interprocedural analysis that checks whether tainted arguments in internal functions flow to return statementsThe receiver taint propagation (e.g.,
req.URL.Query().Get("name")) is not affected — this fix only skipscontext.Contextwhen scanning non-receiver arguments for taint propagation to return values.Tests
Unit tests (
taint/analyzer_internal_test.go)Three new tests for
isContextType:TestIsContextTypeWithContextContext—context.Context(named interface) returnstrueTestIsContextTypeWithPointerToContextContext—*context.Context(pointer-wrapped) returnstrueTestIsContextTypeRejectsNonContextTypes— table-driven:http.Request,string, wrong-package-same-name, right-package-wrong-name, pointer to non-context type,nilall returnfalseIntegration test (
testutils/g705_samples.go)New
SampleCodeG705entry (expected issues: 0) exercises the pattern from this PR:This is exercised by the existing G705 test in
analyzers/analyzers_test.go.Test plan
go test ./...)r.URL.Query().Get("x")directly tohttp.ResponseWriter) is not affected — that taint flows through the receiver chain, not through context argumentsRelated
json.Marshal/strconvsanitizers and interprocedural analysis but did not address thecontext.Contextpropagation vector, which is the dominant source of false positives in HTTP+gRPC codebasesref: #1542