Repository navigation
Conversation
Also taint properties whose accessors wrap a backing field, get-only properties of collection type, and members inherited from base types. Skip constants. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The source modeling incorrectly taints copied/computed properties and non-bindable collections while missing request-data flow through properties backed by inherited fields.
3 open issues
What changed in this PR
This PR broadens CodeQL’s C# ASP.NET remote-source modeling so taint can propagate through more binder-populated model members.
Changes:
- Expands property eligibility to backing-field properties and get-only collections.
- Adds inherited-member traversal and excludes constant fields.
- Extends legacy ASP.NET and ASP.NET Core test fixtures, expected results, and release documentation.
| File | Description |
|---|---|
csharp/ql/test/library-tests/dataflow/flowsources/remote/remoteFlowSource.expected |
Updates expected legacy source and member results. |
csharp/ql/test/library-tests/dataflow/flowsources/remote/RemoteFlowSource.cs |
Adds legacy web-service model cases. |
csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected |
Updates expected ASP.NET Core results. |
csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs |
Adds ASP.NET Core model cases and behavior annotations. |
csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll |
Expands candidate-member rules and shares base-type traversal. |
csharp/ql/lib/change-notes/2026-11-06-taint-plain-data-members.md |
Documents the analysis changes. |
Review effort: Max (auto)
| isPlainDataProperty(p) and | ||
| p.getGetter().isPublic() and | ||
| p.getSetter().isPublic() | ||
| (p.getSetter().isPublic() or p.getType() instanceof CollectionType) |
There was a problem hiding this comment.
Fixed for arrays. ArrayType is excluded from the no-public-setter branch, so string[] Names { get; } no longer qualifies. Arrays with a public setter still do. Added NamesOnlyGet as a negative test. Not fixed: read-only collections such as SynchronizedReadOnlyCollection still qualify, because they inherit from ICollection and CollectionType can't tell them apart. I'm leaving that as a known limitation.

Hello @michaelnebel
Follow-up to #21567 / #21612. Members of types bound from
[WebMethod]or action-method parameters are only tainted when they are public auto-implemented properties or public fields. Real binder-populated types often don't fit that, so taint stops at the first such member and flow is lost. From my security researcher point of view I was not fully satisfied.This PR relaxes
CandidateMemberToTaintand the two transitive classes (AspNetRemoteFlowSourceMember,AspNetCoreRemoteFlowSourceMember):Addto the returned collection. Get-only non-collections stay untainted.isMemberOfBoundType).As noted in the discussion, tainting is type-wide, so these members are tainted wherever the type is used. The change stays narrow for that reason.
Tests extend the existing aspremote and remote flow-source tests. A change note is included.
I'm still not 100% satisfied because for more complex operations in Getter/Setter this would not work. The only way to do it properly would be with TaintTracking I think.
isPlainDataPropertyis purely syntactic: the getter must return an expression that reads a field, and the setter must assign an expression using value to that field. It does not follow accessors with several statements:Local data flow (
DataFlow::localFlow) would handleViaLocaland Conditional, because it follows locals and branches. I confirmed it also handlesf ?? ""and casts, which the syntactic check misses on the getter side. It would missWrapped, because it follows value-preserving steps only, soTrim()andNormalize()end the flow.TaintTracking::localTaintwould follow those. However,TaintedMemberfeeds the taint-step relation, so I expect it to be circular and I haven't tried it.I would like feedback on whether the extra precision is worth a data-flow dependency inside a candidate-member predicate.