Skip to content

csharp: odata lib - #22384

Open
hugo-syn wants to merge 7 commits into
github:mainfrom
hugo-syn:hugo-syn/csharp-odata-tainted-member
Open

csharp: odata lib #22384
hugo-syn wants to merge 7 commits into
github:mainfrom
hugo-syn:hugo-syn/csharp-odata-tainted-member

Conversation

@hugo-syn

Copy link
Copy Markdown

hugo-syn and others added 3 commits August 19, 2026 15:30
Adds semmle.code.csharp.frameworks.OData, following the WCF.qll/JsonNET.qll
convention: values cast, as-converted, or type-tested out of an untyped
ODataActionParameters dictionary, and entities tracked by Delta<T> (via
GetInstance/Patch/Put/CopyChangedValues/CopyUnchangedValues), have no static
type relationship to the action method's own parameter types, so their
members aren't picked up by the existing AspNetRemoteFlowSourceMember
modeling. This adds a TaintedMember for those bound types (with the same
nested-type/collection recursion as AspNetRemoteFlowSourceMember), plus two
AdditionalTaintStep steps for the Delta<T> method calls, which don't fit the
member-read shape TaintedMember covers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Match WCF.qll's convention: only the TaintedMember/AdditionalTaintStep
wiring classes stay private, everything else that identifies a reusable
OData domain concept (ODataActionParametersClass, DeltaClass,
ODataBoundType, DeltaMutatingMethod, DeltaGetInstanceMethod) is public.

Also renames the test fixtures to generic placeholder names.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
import csharp already publicly imports semmle.code.csharp.dataflow.TaintTracking
(and DataFlow), same as WCF.qll/JsonNET.qll rely on implicitly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hugo-syn
hugo-syn requested a review from a team as a code owner August 19, 2026 13:57

@michaelnebel michaelnebel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much! It is really good, if we can get our modelling extended even further!

I have added some initial comments / questions. Maybe OData parameter like types are only relevant for classes that extend ODataController. Should that somehow be incorporated in the logic?

Comment on lines +152 to +160
private class DeltaGetInstanceTaintStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodCall mc |
mc.getTarget().getUnboundDeclaration() instanceof DeltaGetInstanceMethod and
node1.asExpr() = mc.getQualifier() and
node2.asExpr() = mc
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, the QL implementation can be replaced by Models as Data?
Below is the row for one of the GetInstance methods.

extensions:
  - addsTo:
      pack: codeql/csharp-all
      extensible: summaryModel
    data:
      - ["Microsoft.AspNet.OData", "Delta<TStructuralType>", True, "GetInstance", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +3 to +25
namespace Microsoft.AspNet.OData
{
public class ODataActionParameters : Dictionary<string, object>
{
}

public class Delta<TStructuralType> where TStructuralType : class
{
private TStructuralType instance;

public Delta() { instance = default(TStructuralType); }

public TStructuralType GetInstance() => instance;

public void Patch(TStructuralType original) { }

public void Put(TStructuralType original) { }

public void CopyChangedValues(TStructuralType original) { }

public void CopyUnchangedValues(TStructuralType original) { }
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we would like to keep stub implementations separate from the test and store them in test/resources/stubs.
This will require an options file for the test; If possible, it is also preferred, if the test relies fully on stubs and not any .dll files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +97 to +112
private class CandidateODataMember extends Member {
CandidateODataMember() {
this.isPublic() and
not this.isStatic() and
(
this =
any(Property p |
p.isAutoImplemented() and
p.getGetter().isPublic() and
p.getSetter().isPublic()
)
or
this = any(Field f | f.isPublic())
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be a copy of the CandidateMemberToTaint. Perhaps, the implementation from Remote.qll can be re-used?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reused this method, switch to public method

TaintTracking::localExprTaint(any(ODataActionParameterRead r), e)
}

/** The generic `Delta<TStructuralType>` change-tracking class, across OData library versions. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe refer to the unbound declaration with "Delta1" instead of Delta<TStructuralType> as the type parameter is named T for Microsoft.AspNetCore.OData.Deltas.Delta<T>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

hugo-syn added 4 commits August 21, 2026 09:51
Per review feedback on github#22384, replace the hand-written
DeltaGetInstanceMethod/DeltaGetInstanceTaintStep taint step with a
Models-as-Data summaryModel row for both the Microsoft.AspNet.OData and
Microsoft.AspNetCore.OData.Deltas variants of Delta<T>.GetInstance().
Per review feedback on github#22384, OData.qll's CandidateODataMember was an
exact copy of CandidateMemberToTaint from Remote.qll. Make that class
public and import it instead of duplicating it.
Per review feedback on github#22384, keep the ODataActionParameters/Delta<T>
stub implementations out of the test .cs file and store them in
test/resources/stubs instead, following the pattern used by other
frameworks (e.g. JsonNET, Aws). The test now loads the stub project
via an options file and relies on no .dll files.
Per review feedback on github#22384, the doc comment named the type
parameter TStructuralType, but the AspNetCore variant of Delta<T>
names it T. Refer to the unbound generic as \`Delta\`1\`\` instead.
@hugo-syn

Copy link
Copy Markdown
Author

Hi @michaelnebel I think I've made changes for all your requests let me know if it's ok

I'm not sure to get you question:

Maybe OData parameter like types are only relevant for classes that extend ODataController. Should that somehow be incorporated in the logic?

Can you give more details / examples ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants