Skip to content

Python: Model complementary MCP AST handler parameters as ai sources - #22750

Open
Tito0015 wants to merge 7 commits into
github:mainfrom
Tito0015:feature/python-mcp-remote-sources
Open

Tito0015 wants to merge 7 commits into
github:mainfrom
Tito0015:feature/python-mcp-remote-sources

Conversation

@Tito0015

@Tito0015 Tito0015 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR provides complementary native QL AST modeling for Python MCP and fastmcp handlers that still fall outside the API-graph baseline in Mcp.qll after #22703.

Context & Positioning

Following #22822 (legacy queries accept ActiveThreatModelSource), #22703 (now merged) models standard MCP/fastmcp handler parameters and transport sources via QL + MaD. This PR adds the remaining programmatic edge cases only, deduplicated with not isClientParameter(...).

Handlers that MaD / the baseline still miss include:

  • Stacked custom project decorators (e.g. @custom_telemetry above @mcp.tool()), via getADecorator().
  • Nested instance registrations (e.g. @self.mcp.tool()).
  • @staticmethod MCP tools where the first parameter must not be treated as self.

The complementary class McpHandlerParameter extends ThreatModelSource::Range with threat-model kind ai and source type MCP server handler parameter (baseline uses MCP handler parameter). Context parameters are excluded using the upstream isContextAnnotation() logic.

Testing

Merged upstream/main. Local test python/ql/test/library-tests/dataflow/remote-flow-sources/ passes (complementary sources only).

@Tito0015
Tito0015 requested review from a team as code owners October 4, 2026 05:43
Alex-Hofer added a commit to Alex-Hofer/mcp-vulnbench that referenced this pull request Oct 4, 2026


Both proposals for Python MCP sources on the 28 Python cases: the class detects 9, the rows 13, both 13. Records what each covers that the other does not, the method (derived image, checks) and the limits of the comparison.
@Tito0015
Tito0015 force-pushed the feature/python-mcp-remote-sources branch from 2771182 to ec38c23 Compare October 5, 2026 19:46
@Tito0015

Tito0015 commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Hi @geoffw0 — hope you are well.

You reviewed and merged my earlier C++ MMIO PR (#22438); thank you again for that. When you have a moment, I would appreciate a look at this Python follow-up for #22702.

It models MCP / FastMCP server handler parameters as RemoteFlowSources in QL (Mcp.qll), mainly so queries that still take RemoteFlowSource directly (e.g. py/xxe, py/xml-bomb, py/nosql-injection) can see MCP tool/prompt/resource args, and so stacked custom decorators on handlers are covered via getADecorator() (similar to Flask routing).

There is already open MaD coverage in #22703; this PR is intentionally QL-only and complementary. Library tests are under python/ql/test/library-tests/dataflow/remote-flow-sources/ and pass locally with codeql test run.

No rush — happy to adjust based on team preference (QL vs MaD, scope, naming). Thanks for any pointers.

*/

private import python
private import semmle.python.dataflow.new.DataFlow
@geoffw0

geoffw0 commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Hi, this looks reasonable at a glance (though I was a little surprised to see QL-modelled sources rather than MaD models). In any case I've started the tests and am handing over to another member of the team.

@Tito0015
Tito0015 force-pushed the feature/python-mcp-remote-sources branch 4 times, most recently from c909145 to 1ae3030 Compare October 13, 2026 04:14
@owen-mc

owen-mc commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

I was also surprised by the assertion that QL-modelling was needed. I think it's correct though. We should fix it - I think changing those queries (and any others) to use ActiveThreatModelSource instead of RemoteFlowSource would solve it. I've put up this PR to fix it: #22822.

@Tito0015

Copy link
Copy Markdown
Contributor Author

Thanks for the review and for driving this upstream architectural fix in #22822, @owen-mc — migrating those queries to ActiveThreatModelSource is definitely the right long-term move for the Python dataflow pipeline.

I would love to collaborate directly on #22822. I can help benchmark query recall across the affected legacy rules, run test suites against edge cases, or review changes alongside the team.

Once #22822 lands, I will rebase and align this PR (#22750):

  1. MaD Alignment: Shift standard MCP tool, prompt, and resource handler definitions into Models-as-Data (MaD) YAML (coordinating with Python: Add models for the MCP server SDKs (mcp, fastmcp) #22703) so they feed cleanly into ActiveThreatModelSource.
  2. Targeted QL Modeling: Keep the QL logic in Mcp.qll focused strictly on dynamic AST patterns that MaD cannot currently express—specifically stacked and custom wrapper decorators (getADecorator()), class/instance registrations (@self.mcp.tool(), add_tool), and strict parameter exclusions (e.g., Context types).

With @Alex-Hofer already referencing this work in mcp-vulnbench, there is clear momentum to establish robust, high-recall taint tracking for Agentic AI and MCP server interfaces. I’m very keen to help the CodeQL team establish standard security coverage across this emerging attack surface.

For background on my work in AST/CPG query lowering and static analysis compiler infrastructure:

Let me know the best way to coordinate on #22822—I'm happy to push validation runs or draft test cases against your branch whenever helpful!

@owen-mc

owen-mc commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Thanks for the offer, @Tito0015 , but it isn't needed. #22822 has now been merged.

@Tito0015
Tito0015 force-pushed the feature/python-mcp-remote-sources branch from 410350c to b33c676 Compare October 15, 2026 19:00
@Tito0015 Tito0015 changed the title Python: Model MCP and FastMCP server handler parameters as remote sources (#22702) Python: Model complex MCP AST handler parameters as remote sources (Complementary to #22703) Oct 15, 2026
@Tito0015

Copy link
Copy Markdown
Contributor Author

Awesome to see #22822 land, @owen-mc! Great work resolving that across the query suite.

I have rebased this PR onto main and narrowed Mcp.qll strictly to the dynamic AST patterns that MaD cannot currently express—specifically stacked custom project decorators (getADecorator()) and instance registrations (@self.mcp.tool()).

This keeps the PR lightweight and complementary to #22703 without duplicating the baseline YAML definitions. Ready for review whenever the team has bandwidth!

@Tito0015
Tito0015 force-pushed the feature/python-mcp-remote-sources branch from b33c676 to 86d2f8e Compare October 16, 2026 23:47
@Tito0015
Tito0015 force-pushed the feature/python-mcp-remote-sources branch from 86d2f8e to d0a798c Compare October 19, 2026 18:46
@owen-mc

owen-mc commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating the PR. Now that it's ready for review, please don't force-push again, as that makes it harder to review.

@Tito0015

Copy link
Copy Markdown
Contributor Author

Understood, @owen-mc! Leaving git history as-is and won't force-push. Thanks for taking a look whenever you have the bandwidth.

@Tito0015

Tito0015 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

@owen-mc Gentle nudge — merged latest main in 40805f5 without force-pushing, per your request. Tests still green. No rush; thanks for reviewing when you can

not isMcpContextParameter(p)
}

private class McpHandlerParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode {

@owen-mc owen-mc Nov 4, 2026 •

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.

Suggested change
private class McpHandlerParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode {
private class McpHandlerParameter extends ThreatModelSource::Range, DataFlow::ParameterNode {

I assume we want this to have kind ai, instead of remote. So, instead you should extend ThreatModelSource::Range and add the predicate override string getThreatModel() { result = "ai" }.

or
p = handler.getKwarg()
) and
not (handler.isMethod() and p = handler.getArg(0)) and

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.

We do want the first argument to static methods. (isMethod holds if handler is declared inside a class.) Add private import semmle.python.dataflow.new.internal.DataFlowDispatch at the top and make this change:

Suggested change
not (handler.isMethod() and p = handler.getArg(0)) and
not (
handler.isMethod() and
not isStaticmethod(handler) and
p = handler.getArg(0)
) and

Ideally you'd add a test for this as well.

exists(API::Node ctx |
ctx in [
API::moduleImport("fastmcp").getMember("Context"),
API::moduleImport("mcp").getMember("server").getMember("fastmcp").getMember("Context"),

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.

Suggested change
API::moduleImport("mcp").getMember("server").getMember("fastmcp").getMember("Context"),
API::moduleImport("fastmcp").getMember("server").getMember("context").getMember("Context"),
API::moduleImport("mcp").getMember("server").getMember("fastmcp").getMember("Context"),
API::moduleImport("mcp")
.getMember("server")
.getMember("fastmcp")
.getMember("server")
.getMember("Context"),

Apply review feedback: ThreatModelSource kind ai, expanded Context API paths, isStaticmethod parameter handling, and staticmethod test coverage.
Copilot AI dynamic review requested due to automatic review settings November 4, 2026 16:54
@Tito0015

Tito0015 commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the clear feedback, @owen-mc! All items have been addressed in 99cdc47 (pushed normally without force-pushing):

  • Threat Model Source: McpHandlerParameter now extends ThreatModelSource::Range with override string getThreatModel() { result = "ai" }.
  • Static Methods: Imported DataFlowDispatch and adjusted parameter exclusions with not isStaticmethod(handler).
  • Context API Paths: Added the full alternative import paths for Context (fastmcp.server.context.Context and mcp.server.fastmcp.server.Context).
  • Tests & Docs: Added a @staticmethod tool test case in mcp_test.py, retargeted RemoteFlowSources.ql to select ThreatModelSource with kind "ai", verified the test suite passes locally (codeql test run), and updated the change note.

Also merged latest upstream/main cleanly. Ready for your review whenever you have time!

Copilot AI 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.

🟡 Changes recommended

The MCP model misclassifies injected Context parameters and drops valid handler inputs, while the regression query would produce unexpected rows when merged with the companion AI-source changes.

6 open issues
What changed in this PR

Adds native Python AST modeling for complex mcp and fastmcp handler registrations, complementing #22703’s Models-as-Data coverage under the opt-in ai threat model.

Changes:

  • Recognizes stacked decorators, instance lookups, and static handlers.
  • Models handler parameters as sources while excluding recognized Context annotations.
  • Adds regression fixtures, expected results, and framework-support documentation.
File Description
python/​ql/​test/​library-tests/​dataflow/​remote-flow-sources/​RemoteFlowSources.ql Queries AI sources for regression testing.
python/​ql/​test/​library-tests/​dataflow/​remote-flow-sources/​RemoteFlowSources.expected Records four expected native parameter sources.
python/​ql/​test/​library-tests/​dataflow/​remote-flow-sources/​mcp_test.py Exercises handler registrations and Context exclusions.
python/​ql/​lib/​semmle/​python/​frameworks/​Mcp.qll Implements handler recognition and parameter-source modeling.
python/​ql/​lib/​semmle/​python/​Frameworks.qll Loads the MCP model with existing framework models.
python/​ql/​lib/​change-notes/​2026-10-04-mcp-remote-sources.md Announces complementary opt-in AI-source coverage.
docs/​codeql/​reusables/​supported-frameworks.rst Lists both MCP packages as supported frameworks.

Review effort: Max (auto)

Comment on lines +4 to +6
from ThreatModelSource src
where src.getThreatModel() = "ai" and exists(src.getLocation().getFile().getRelativePath())
select src, src.getSourceType()
.getMember("server")
.getMember("fastmcp")
.getMember("server")
.getMember("Context"),
Comment on lines +111 to +112
exists(Expr ann | ann = p.getAnnotation() |
isContextAnnotation(ann)
exists(Expr ann | ann = p.getAnnotation() |
isContextAnnotation(ann)
or
exists(Subscript sub | sub = ann | isContextAnnotation(sub.getValue()))
Comment on lines +131 to +137
) and
not (
handler.isMethod() and
not isStaticmethod(handler) and
p = handler.getArg(0)
) and
not isMcpContextParameter(p)
Comment on lines +11 to +12
def other_decorator(fn):
return fn
@owen-mc

owen-mc commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

I think it's easiest to wait until github/codeql#22703 is merged and then either rebase onto main or merge in main, as there is now a bit of overlap between them.

@Tito0015

Tito0015 commented Nov 8, 2026

Copy link
Copy Markdown
Contributor Author

Makes total sense, @owen-mc. I'll keep an eye on #22703 and rebase / merge main in as soon as it lands so we can align the tests cleanly. Thanks!

@owen-mc

owen-mc commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

#22703 has now been merged.

@Tito0015 Tito0015 changed the title Python: Model complex MCP AST handler parameters as remote sources (Complementary to #22703) Python: Model complementary MCP AST handler parameters as ai sources Nov 14, 2026
@Tito0015

Copy link
Copy Markdown
Contributor Author

Thanks for the heads-up, @owen-mc! Merged upstream/main cleanly into the branch (no force-push) and verified everything locally on CodeQL 2.27.4:

Ready for your review whenever you have bandwidth!

@Tito0015
Tito0015 requested a review from owen-mc November 14, 2026 01:56

This branch has not been deployed

No deployments
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.

5 participants