Compare commits

..

72 Commits

Author SHA1 Message Date
Owen Mansel-Chan
376c783f67 Fix ConditionGuardNode 2026-06-03 15:09:42 +01:00
Owen Mansel-Chan
e781af9b0d Accept test output change (location) 2026-06-03 14:52:09 +01:00
Owen Mansel-Chan
47a8c5d9a7 Accept test output change names 2026-06-03 14:49:49 +01:00
Owen Mansel-Chan
f84e8b0fbc Make all lines in logging tests reachable 2026-06-03 14:29:10 +01:00
Owen Mansel-Chan
d7ff3ff472 Restore ConditionGuardNode 2026-06-03 14:19:32 +01:00
Owen Mansel-Chan
5c91529b92 Fix treatment of ParenExpr 2026-06-03 14:19:30 +01:00
Owen Mansel-Chan
996a936f85 Add Go CFG consistency query 2026-06-03 14:19:28 +01:00
Owen Mansel-Chan
e56ca68821 Go: update expected node names 2026-06-03 13:04:32 +01:00
Owen Mansel-Chan
6d3d220f71 Use shared CFG getIfInit 2026-06-03 13:04:30 +01:00
Owen Mansel-Chan
7aa651a6c7 Fix lit-init nodes
There was a loop in the definitions which meant nothing ever got made
2026-06-03 13:04:28 +01:00
Owen Mansel-Chan
ee6b8394ab Fix range loop CFG 2026-06-03 13:04:26 +01:00
Owen Mansel-Chan
d8ac19e8c2 update function-entry additional nodes 2026-06-03 13:04:24 +01:00
Owen Mansel-Chan
5578987d0a Restore ExprNode for FuncLit 2026-06-03 13:04:22 +01:00
Owen Mansel-Chan
b6813ac20f Accept change in test output
To determine that test9 can't return normally, you have to use the fact
that test5 can't return normally. This would make CFG construction
recursive, which would be bad for performance. Therefore we accept the
limitation that we cannot detect that test9 can't return normally, and
we change the test output.
2026-06-03 13:04:20 +01:00
Owen Mansel-Chan
b12f7915f9 Fix empty switch statements 2026-06-03 13:04:18 +01:00
Owen Mansel-Chan
e2516fc383 Control flow shouldn't enter another callable 2026-06-03 13:04:16 +01:00
Owen Mansel-Chan
44e7f0fc6d Fix CFG for return instructions 2026-06-03 13:04:15 +01:00
Owen Mansel-Chan
c489f6ffa1 Produce CFG nodes for more reference expressions, like selector bases 2026-06-03 13:04:13 +01:00
Owen Mansel-Chan
c620a423d5 Fix global value numbering calculation 2026-06-03 13:04:10 +01:00
Owen Mansel-Chan
a23b398be9 Include receivers in parameter init 2026-06-03 13:04:09 +01:00
Owen Mansel-Chan
bee9ead570 Fix CFG for range loop 2026-06-03 13:04:07 +01:00
Owen Mansel-Chan
481564668d Fix CFG for select statements 2026-06-03 13:04:05 +01:00
Owen Mansel-Chan
d189d28f63 Use shared CFG implementation of for loops 2026-06-03 13:04:03 +01:00
Owen Mansel-Chan
b411b0ecd0 Do not include comments in the CFG 2026-06-03 13:04:01 +01:00
Owen Mansel-Chan
2c4fe160bf Fix edges to function exit with result variables 2026-06-03 13:03:59 +01:00
Owen Mansel-Chan
0faf3c89df Tweak getEnclosingCallable 2026-06-03 13:03:57 +01:00
Owen Mansel-Chan
b01c0c524b Model non-returning functions in CFG 2026-06-03 13:03:55 +01:00
Owen Mansel-Chan
82913576d6 Create cfg node for child of ParenExpr 2026-06-03 13:03:53 +01:00
Owen Mansel-Chan
c054c3e9b5 Add go/print-cfg 2026-06-03 13:03:51 +01:00
Owen Mansel-Chan
7538201824 Fix overlay annotation in PrintAst.qll 2026-06-03 13:03:49 +01:00
copilot-swe-agent[bot]
e8310f4805 Add GotoStmt, Parameter, Throw, Assignment and PatternMatchExpr to Go Ast module for updated shared CFG AstSig
Agent-Logs-Url: https://github.com/github/codeql/sessions/005953d9-1d54-4071-8cd2-5a7d26f65f61

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-06-03 13:03:48 +01:00
copilot-swe-agent[bot]
f239a8cbce Fix shared Go CFG expression and return-edge regressions
Agent-Logs-Url: https://github.com/github/codeql/sessions/3f96ead2-cda4-479c-9e37-f38ace035870

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-06-03 13:03:46 +01:00
copilot-swe-agent[bot]
13cbc84ded Add missing QLdoc comments to IR.qll classes, predicates, and newtype branches
Agent-Logs-Url: https://github.com/github/codeql/sessions/12c0f9c8-1b85-4db4-b26f-8dbce818aa5a

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-06-03 13:03:44 +01:00
copilot-swe-agent[bot]
0c2d12b6b0 Add File as a CFG root to support file-level declarations
Agent-Logs-Url: https://github.com/github/codeql/sessions/2867f173-c3f1-4564-9885-3ff4517bbf30

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-06-03 13:03:42 +01:00
copilot-swe-agent[bot]
2731bce761 Fix compiler errors and warnings: remove redundant type checks and isIn guards, mark implicitFieldSelection as additional
Agent-Logs-Url: https://github.com/github/codeql/sessions/8d96128c-44db-4ba1-9eca-1a5b37f54442

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-06-03 13:03:40 +01:00
copilot-swe-agent[bot]
171ec3cd90 Fix overlay annotation in ControlFlowGraphShared.qll
Agent-Logs-Url: https://github.com/github/codeql/sessions/011f8465-5ad3-44e2-9d84-f5314ea1afb2

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-06-03 13:03:38 +01:00
copilot-swe-agent[bot]
5eb47af3c2 Continue shared CFG library migration: fix prologue/epilogue, implicit fields, cleanup
Agent-Logs-Url: https://github.com/github/codeql/sessions/011f8465-5ad3-44e2-9d84-f5314ea1afb2

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-06-03 13:03:36 +01:00
Owen Mansel-Chan
367de60ca8 Checkpoint from VS Code for cloud agent session 2026-06-03 13:03:34 +01:00
Owen Mansel-Chan
0e97c26da6 All 90 queries compile cleanly. Here's the status:
Phase 2 progress — shared CFG library fully instantiated with IR support:

ControlFlowGraphShared.qll (1290 lines) now contains:

additionalNode entries for all 30+ synthetic IR operations:

Assignment writes (assign:i), compound RHS (compound-rhs), tuple extraction (extract:i)
Zero initialization (zero-init:i), increment/decrement (implicit-one, incdec-rhs)
Return (return), result write/read (result-write:i, result-read:i)
Function prologue (param-init:i, arg:i, result-init:i, result-zero-init:i)
Range next (next), send (send), implicit deref (implicit-deref)
Slice bounds (implicit-low/high/max), case checks (case-check:i)
Type switch variables (type-switch-var), literal init (lit-init, lit-index)
Implicit field selection (implicit-field:i)
step routing for all constructs needing custom flow:

Assignments (with epilogue node sequencing for extract/assign chains)
Increment/decrement (operand → implicit-one → incdec-rhs → In)
Return statements (expressions → extract → result-write → return)
Index/slice/selector expressions (with implicit deref insertion)
Composite literals (pre-order: allocation first, then element init)
Send statements (channel → value → send)
Plus the existing: if-with-init, for/range loops, switch/select, defer, go
What remains for full migration (making shared types primary):

Rewrite ControlFlowGraph.qll: change ControlFlow::Node from TControlFlowNode to GoCfg::ControlFlowNode
Rewrite IR.qll (1598 lines): map all 30+ IR instruction classes to shared CFG nodes via getAstNode()/getIdTag() instead of Mk* types
Update BasicBlocks.qll to use shared library's built-in BasicBlock
Update ~30 downstream files (SSA, DataFlow, security queries) that reference IR::Instruction/ControlFlow::Node
Add function prologue/epilogue step routing (param init, result init, defer LIFO at exit)
Remove ControlFlowGraphImpl.qll
This remaining work is a substantial rewrite (3000+ lines across 35+ files) that would benefit from being done incrementally with test validation at each step. Shall I continue with the IR.qll rewrite next?
2026-06-03 13:03:32 +01:00
Owen Mansel-Chan
23675b0fae Phase 1 — Complete. The shared CFG library is now instantiated and wired into Go:
Created ControlFlowGraphShared.qll — contains the full GoCfg module with:

Ast module implementing AstSig<Location> — maps Go's AST types to the shared interface, with empty types for constructs Go doesn't have (while/do/try/catch/throw/ternary/null-coalescing)
Input module implementing InputSig1 + InputSig2 — labels, conditional contexts, custom step() predicates for if-with-init, for/range loops, expression/type switches, case clauses, select, defer, and go statements; abrupt completions for panics, fallthrough, and goto
Make0/Make1/Make2 wiring and import Public
Modified ControlFlowGraph.qll — added import of the shared library and re-exports it as module SharedCfg = GoCfg, making shared types accessible as SharedCfg::ControlFlowNode, SharedCfg::ControlFlow::EntryNode, SharedCfg::BasicBlock, etc.

No breakage — all 90 src queries and library files compile cleanly.
2026-06-03 13:03:30 +01:00
Owen Mansel-Chan
2b26022e4b The shared CFG library instantiation for Go compiles cleanly. Let me show you the current state of the file:
Read ControlFlowGraphShared.qll, lines 1 to 50

The file compiles successfully. Here's a summary of what was implemented:

ControlFlowGraphShared.qll — Shared CFG library instantiation for Go, wrapped in the GoCfg module to coexist with the existing CFG during transition. Key design decisions:

Ast module (implements AstSig):

Callable = FuncDef with a body
IfStmt — only For if-statements without init (those with init use custom steps)
LoopStmt — uses characteristic predicate pattern (not direct extension) to avoid return type conflicts with BlockStmt
Empty classes for: WhileStmt, DoStmt, ForStmt, ForeachStmt, ThrowStmt, TryStmt, CatchClause, Switch, Case, ConditionalExpr, NullCoalescingExpr (Go doesn't have these constructs)
LogicalAndExpr = LandExpr, LogicalOrExpr = LorExpr, LogicalNotExpr = NotExpr
BooleanLiteral maps to true/false identifiers
skipCfg filters out type expressions, non-rvalue references, parenthesized expressions, etc.
Input module (implements InputSig1 + InputSig2):

Labels: Go statement labels + fallthrough
Custom steps for: if-with-init, for loops, range loops, expression/type switch, case clauses, select statements, defer, go statements
Abrupt completions: panic via calls/div/deref/type-assert/index/conversion, fallthrough, goto
End abrupt completions: labeled statements catch breaks, goto targets
Remaining work (todos 4-6): The GoCfg module is self-contained and doesn't yet replace the existing CFG. Integration with BasicBlocks.qll, IR.qll, and the public ControlFlowGraph.qll API is the next phase — which is a larger effort requiring updates to DataFlow and all downstream consumers.
2026-06-03 13:03:29 +01:00
Owen Mansel-Chan
52f2a5825a Merge pull request #21804 from github/copilot/add-tests-for-models
Java: Update CWE-918 model coverage for Apache HttpClient `execute` sinks
2026-06-03 12:55:56 +01:00
Owen Mansel-Chan
d55ff83568 Merge pull request #21269 from owen-mc/go/improve-tests-for-varargs-flow
Go: improve tests for varargs flow
2026-06-03 12:39:46 +01:00
Asger F
7edf0100cc Merge pull request #21924 from asgerf/asgerf/yeast-changes
Yeast: some fixes
2026-06-03 10:32:38 +02:00
Owen Mansel-Chan
167c837088 Merge pull request #21914 from owen-mc/shared/cfg/if-init
Shared CFG: allow init stmts for IfStmt
2026-06-02 22:01:02 +01:00
Jeroen Ketema
d11fc3a00e Merge pull request #21932 from jketema/jketema/vue
JS: Add Vue to `file_coverage_languages` and `github_api_languages`
2026-06-02 17:53:25 +02:00
Jeroen Ketema
9d5dfea5c5 JS: Add Vue to file_coverage_languages and github_api_languages 2026-06-02 16:57:51 +02:00
Owen Mansel-Chan
be9c785cb2 Fix incorrect QLDoc
Co-authored-by: Tom Hvitved <hvitved@github.com>
2026-06-02 09:26:13 +01:00
Asger F
3f3bed62d3 yeast: type-check for missing required fields
Add FieldCardinality to Schema to track required/multiple per field,
populated from the ast_types.yml suffixes (bare = required single,
? = optional single, + = required multiple, * = optional multiple).

dump_ast_with_type_errors now emits:
  <-- ERROR: missing required field 'name'
for any node in the output AST whose declared schema requires a field
that is absent from the actual node.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 14:18:37 +02:00
Asger F
21f216af8c yeast-macros: omit empty fields produced by .. splice
When a {..expr} splice in an output template is empty (e.g. from an
optional capture that did not match), drop the field entirely rather
than emitting an empty named field. This lets a single rule with
optional captures replace what used to be two near-identical rules.

Also re-renders the corpus to drop the now-suppressed empty fields.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-01 14:18:37 +02:00
Asger F
1751d70c62 Fix parsing of corpus tests when --- delimiter is missing 2026-06-01 14:18:37 +02:00
Asger F
ac8eb50c26 Yeast: Allow 'r#type' to escape the 'type' keyword in macro 2026-06-01 14:18:37 +02:00
Asger F
1ecdc3614f Yeast: Fix matching against extras like comments 2026-06-01 14:18:37 +02:00
Asger F
e3b3888bee Yeast: Fix handling of captures with multiple results 2026-06-01 14:18:36 +02:00
Asger F
ef9306d82c Yeast: Allow rules that return an empty sequence 2026-06-01 14:18:36 +02:00
Asger F
56822f8ee1 Tree-sitter-extactor: More helpful panic message 2026-06-01 14:04:49 +02:00
Owen Mansel-Chan
5e5a0437e1 Shared CFG: allow init stmts for IfStmt 2026-05-30 07:35:29 +01:00
Owen Mansel-Chan
d95d99848c Build RequestBuilder more realistically 2026-05-28 11:05:40 +01:00
Owen Mansel-Chan
8937e22735 Add summary models for org.apache.http.client.methods.RequestBuilder
Generated by GPT 5.3-codex, verified by me.
2026-05-28 10:56:37 +01:00
Owen Mansel-Chan
37589dd8a0 Improve how org.apache.http.client.HttpClient is created in test 2026-05-28 10:30:43 +01:00
Owen Mansel-Chan
a159dc1c66 Change variable name in test 2026-05-28 10:28:14 +01:00
Owen Mansel-Chan
dc864762c3 Add change note 2026-05-07 10:23:50 +01:00
Owen Mansel-Chan
dd35bc0722 Update test output 2026-05-07 10:17:47 +01:00
copilot-swe-agent[bot]
043ec857ab Replace fluent SSRF changes with Apache HttpClient execute model tests
Agent-Logs-Url: https://github.com/github/codeql/sessions/3db201db-a1b5-4353-a94a-14a8d156dd3b

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-05-06 20:31:34 +00:00
copilot-swe-agent[bot]
f5b17b0b48 Add SSRF tests and stubs for Apache Http fluent Request models
Agent-Logs-Url: https://github.com/github/codeql/sessions/bd4fa112-dbc3-47e8-9cef-9b1b13c7e549

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-05-06 16:08:02 +00:00
copilot-swe-agent[bot]
26dca558c7 Initial plan 2026-05-06 16:02:08 +00:00
Owen Mansel-Chan
57ce0b3d51 Accept data flow consistency result 2026-02-05 22:28:54 +00:00
Owen Mansel-Chan
408ba2e139 (Misc) Delete spuriously committed binary file 2026-02-04 13:41:14 +00:00
Owen Mansel-Chan
7632bdba88 (Misc) fix variable names 2026-02-04 13:40:52 +00:00
Owen Mansel-Chan
4b830c1864 Test varargs flow with models-as-data 2026-02-04 13:40:33 +00:00
Owen Mansel-Chan
d6c8767647 Test flow out of varargs parameter in function model 2026-02-04 13:40:01 +00:00
Owen Mansel-Chan
ec815397a2 Test flow out of varargs parameter in source function 2026-02-04 13:38:48 +00:00
464 changed files with 10520 additions and 15551 deletions

View File

@@ -0,0 +1,3 @@
import go
private import semmle.go.controlflow.ControlFlowGraphShared
import GoCfg::ControlFlow::Consistency

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* The Go control flow graph implementation has been migrated to use the shared CFG library. This is an internal change with no user-visible API changes.

53
go/ql/lib/printCfg.ql Normal file
View File

@@ -0,0 +1,53 @@
/**
* @name Print CFG
* @description Produces a representation of a file's Control Flow Graph.
* This query is used by the VS Code extension.
* @id go/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
*/
import go
import semmle.go.controlflow.ControlFlowGraph
private import semmle.go.controlflow.ControlFlowGraphShared
external string selectedSourceFile();
private predicate selectedSourceFileAlias = selectedSourceFile/0;
external int selectedSourceLine();
private predicate selectedSourceLineAlias = selectedSourceLine/0;
external int selectedSourceColumn();
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
module ViewCfgQueryInput implements GoCfg::ControlFlow::ViewCfgQueryInputSig<File> {
predicate selectedSourceFile = selectedSourceFileAlias/0;
predicate selectedSourceLine = selectedSourceLineAlias/0;
predicate selectedSourceColumn = selectedSourceColumnAlias/0;
predicate cfgScopeSpan(
CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn
) {
file = scope.getFile() and
scope.getLocation().getStartLine() = startLine and
scope.getLocation().getStartColumn() = startColumn and
exists(Location loc |
loc.getEndLine() = endLine and
loc.getEndColumn() = endColumn and
loc = scope.(FuncDef).getBody().getLocation()
)
or
file = scope.(File) and
startLine = 1 and
startColumn = 1 and
endLine = file.getNumberOfLines() and
endColumn = 999999
}
}
import GoCfg::ControlFlow::ViewCfgQuery<File, ViewCfgQueryInput>

View File

@@ -431,7 +431,7 @@ private class HeuristicLoggerFunction extends Method {
)
}
override predicate mayReturnNormally() { logFunctionPrefix != "Fatal" }
override predicate mustNotReturnNormally() { logFunctionPrefix = "Fatal" }
override predicate mustPanic() { logFunctionPrefix = "Panic" }
}

View File

@@ -1,7 +1,7 @@
/**
* Provides queries to pretty-print a Go AST as a graph.
*/
overlay[local]
overlay[local?]
module;
import go

View File

@@ -437,11 +437,12 @@ class Function extends ValueEntity, @functionobject {
* This predicate is an over-approximation: it may hold for functions that can never
* return normally, but it never fails to hold for functions that can.
*
* Note this is declared here and not in `DeclaredFunction` so that library models can override this
* by extending `Function` rather than having to remember to extend `DeclaredFunction`.
* Library models should not override this predicate; override `mustNotReturnNormally`
* instead, so that the control-flow graph construction can take the model into account.
*/
predicate mayReturnNormally() {
not this.mustPanic() and
not this.mustNotReturnNormally() and
(ControlFlow::mayReturnNormally(this.getFuncDecl()) or not exists(this.getBody()))
}
@@ -461,6 +462,16 @@ class Function extends ValueEntity, @functionobject {
*/
predicate mustPanic() { none() }
/**
* Holds if calling this function never returns normally (for example because it
* always panics, exits the process, or loops forever).
*
* Unlike `mayReturnNormally`, this predicate must be defined without reference to
* the control-flow graph, so that it can be used during CFG construction to
* suppress normal-flow successors of calls to this function.
*/
predicate mustNotReturnNormally() { none() }
/** Gets the number of parameters of this function. */
int getNumParameter() { result = this.getType().(SignatureType).getNumParameter() }

View File

@@ -5,66 +5,27 @@ overlay[local]
module;
import go
private import ControlFlowGraphImpl
private import codeql.controlflow.BasicBlock as BB
private import codeql.controlflow.SuccessorType
private import ControlFlowGraphShared
private module Input implements BB::InputSig<Location> {
/** A delineated part of the AST with its own CFG. */
class CfgScope = ControlFlow::Root;
/** A basic block in the control-flow graph. */
class BasicBlock = GoCfg::Cfg::BasicBlock;
/** The class of control flow nodes. */
class Node = ControlFlowNode;
/** Gets the CFG scope in which this node occurs. */
CfgScope nodeGetCfgScope(Node node) { node.getRoot() = result }
/** Gets an immediate successor of this node. */
Node nodeGetASuccessor(Node node, SuccessorType t) {
result = node.getASuccessor() and
(
not result instanceof ControlFlow::ConditionGuardNode and t instanceof DirectSuccessor
or
t.(BooleanSuccessor).getValue() = result.(ControlFlow::ConditionGuardNode).getOutcome()
)
}
/**
* Holds if `node` represents an entry node to be used when calculating
* dominance.
*/
predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode }
/**
* Holds if `node` represents an exit node to be used when calculating
* post dominance.
*/
predicate nodeIsPostDominanceExit(Node node) { node instanceof ExitNode }
}
private module BbImpl = BB::Make<Location, Input>;
class BasicBlock = BbImpl::BasicBlock;
class EntryBasicBlock = BbImpl::EntryBasicBlock;
cached
private predicate reachableBB(BasicBlock bb) {
bb instanceof EntryBasicBlock
or
exists(BasicBlock predBB | predBB.getASuccessor(_) = bb | reachableBB(predBB))
}
/** An entry basic block. */
class EntryBasicBlock = GoCfg::Cfg::EntryBasicBlock;
/**
* A basic block that is reachable from an entry basic block.
*
* Since the shared CFG library only creates nodes for reachable code,
* all basic blocks are reachable by construction.
*/
class ReachableBasicBlock extends BasicBlock {
ReachableBasicBlock() { reachableBB(this) }
ReachableBasicBlock() { any() }
}
/**
* A reachable basic block with more than one predecessor.
*/
class ReachableJoinBlock extends ReachableBasicBlock {
ReachableJoinBlock() { this.getFirstNode().isJoin() }
ReachableJoinBlock() { this.getFirstNode().(ControlFlow::Node).isJoin() }
}

View File

@@ -5,13 +5,17 @@ overlay[local]
module;
import go
private import ControlFlowGraphImpl
private import ControlFlowGraphShared
/** Provides helper predicates for mapping btween CFG nodes and the AST. */
/** Provides helper predicates for mapping between CFG nodes and the AST. */
module ControlFlow {
/** A file or function with which a CFG is associated. */
class Root extends AstNode {
Root() { exists(this.(File).getADecl()) or exists(this.(FuncDef).getBody()) }
Root() {
exists(this.(FuncDef).getBody())
or
exists(this.(File).getADecl())
}
/** Holds if `nd` belongs to this file or function. */
predicate isRootOf(AstNode nd) {
@@ -29,22 +33,16 @@ module ControlFlow {
}
/**
* A node in the intra-procedural control-flow graph of a Go function or file.
* A node in the intra-procedural control-flow graph of a Go function.
*
* Nodes correspond to expressions and statements that compute a value or perform
* an operation (as opposed to providing syntactic structure or type information).
*
* There are also synthetic entry and exit nodes for each Go function and file
* There are also synthetic entry and exit nodes for each Go function
* that mark the beginning and the end, respectively, of the execution of the
* function and the loading of the file.
* function.
*/
class Node extends TControlFlowNode {
/** Gets a node that directly follows this one in the control-flow graph. */
Node getASuccessor() { result = CFG::succ(this) }
/** Gets a node that directly precedes this one in the control-flow graph. */
Node getAPredecessor() { this = result.getASuccessor() }
class Node extends GoCfg::ControlFlowNode {
/** Holds if this is a node with more than one successor. */
predicate isBranch() { strictcount(this.getASuccessor()) > 1 }
@@ -52,22 +50,23 @@ module ControlFlow {
predicate isJoin() { strictcount(this.getAPredecessor()) > 1 }
/** Holds if this is the first control-flow node in `subtree`. */
predicate isFirstNodeOf(AstNode subtree) { CFG::firstNode(subtree, this) }
predicate isFirstNodeOf(AstNode subtree) {
this.isBefore(subtree)
or
this.injects(subtree)
}
/** Holds if this node is the (unique) entry node of a function or file. */
predicate isEntryNode() { this instanceof MkEntryNode }
/** Holds if this node is the (unique) entry node of a function. */
predicate isEntryNode() { this instanceof GoCfg::ControlFlow::EntryNode }
/** Holds if this node is the (unique) exit node of a function or file. */
predicate isExitNode() { this instanceof MkExitNode }
/** Gets the basic block to which this node belongs. */
BasicBlock getBasicBlock() { result.getANode() = this }
/** Holds if this node is the (unique) exit node of a function. */
predicate isExitNode() { this instanceof GoCfg::ControlFlow::ExitNode }
/** Holds if this node dominates `dominee` in the control-flow graph. */
overlay[caller?]
pragma[inline]
predicate dominatesNode(ControlFlow::Node dominee) {
exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j |
exists(GoCfg::Cfg::BasicBlock thisbb, GoCfg::Cfg::BasicBlock dbb, int i, int j |
this = thisbb.getNode(i) and dominee = dbb.getNode(j)
|
thisbb.strictlyDominates(dbb)
@@ -76,20 +75,12 @@ module ControlFlow {
)
}
/** Gets the innermost function or file to which this node belongs. */
Root getRoot() { none() }
/** Gets the innermost function to which this node belongs. */
Root getRoot() { result = this.getEnclosingCallable() }
/** Gets the file to which this node belongs. */
File getFile() { result = this.getLocation().getFile() }
/**
* Gets a textual representation of this control flow node.
*/
string toString() { result = "control-flow node" }
/** Gets the source location for this element. */
Location getLocation() { none() }
/**
* DEPRECATED: Use `getLocation()` instead.
*
@@ -113,6 +104,22 @@ module ControlFlow {
}
}
/** A synthetic entry node for a function. */
class EntryNode extends Node instanceof GoCfg::ControlFlow::EntryNode { }
/** A synthetic exit node for a function. */
class ExitNode extends Node instanceof GoCfg::ControlFlow::ExitNode { }
private predicate isBranchConditionRoot(Expr expr) {
expr = any(LogicalBinaryExpr lbe).getLeftOperand()
or
expr = any(ForStmt fs).getCond()
or
expr = any(IfStmt is).getCond()
or
expr = any(ExpressionSwitchStmt ess | not exists(ess.getExpr())).getACase().getAnExpr()
}
/**
* A control-flow node that initializes or updates the value of a constant, a variable,
* a field, or an (array, slice, or map) element.
@@ -172,7 +179,7 @@ module ControlFlow {
exists(IR::FieldTarget trg | trg = super.getLhs() |
(
trg.getBase() = base or
trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr())
trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr())
) and
trg.getField() = f and
super.getRhs() = rhs
@@ -220,7 +227,7 @@ module ControlFlow {
exists(IR::ElementTarget trg | trg = super.getLhs() |
(
trg.getBase() = base or
trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr())
trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr())
) and
trg.getIndex() = index and
super.getRhs() = rhs
@@ -250,11 +257,19 @@ module ControlFlow {
* A control-flow node recording the fact that a certain expression has a known
* Boolean value at this point in the program.
*/
class ConditionGuardNode extends IR::Instruction, MkConditionGuardNode {
class ConditionGuardNode extends IR::Instruction {
Expr cond;
boolean outcome;
ConditionGuardNode() { this = MkConditionGuardNode(cond, outcome) }
ConditionGuardNode() {
isBranchConditionRoot(cond) and
this.isAfterTrue(cond) and
outcome = true
or
isBranchConditionRoot(cond) and
this.isAfterFalse(cond) and
outcome = false
}
private predicate ensuresAux(Expr expr, boolean b) {
expr = cond and b = outcome
@@ -320,21 +335,17 @@ module ControlFlow {
boolean getOutcome() { result = outcome }
override Root getRoot() { result.isRootOf(cond) }
override string toString() { result = cond + " is " + outcome }
override Location getLocation() { result = cond.getLocation() }
}
/**
* Gets the entry node of function or file `root`.
* Gets the entry node of function `root`.
*/
Node entryNode(Root root) { result = MkEntryNode(root) }
EntryNode entryNode(Root root) { result.getEnclosingCallable() = root }
/**
* Gets the exit node of function or file `root`.
* Gets the exit node of function `root`.
*/
Node exitNode(Root root) { result = MkExitNode(root) }
ExitNode exitNode(Root root) { result.getEnclosingCallable() = root }
/**
* Holds if the function `f` may return without panicking, exiting the process, or looping forever.
@@ -342,7 +353,12 @@ module ControlFlow {
* This is defined conservatively, and so may also hold of a function that in fact
* cannot return normally, but never fails to hold of a function that can return normally.
*/
predicate mayReturnNormally(FuncDecl f) { CFG::mayReturnNormally(f.getBody()) }
predicate mayReturnNormally(FuncDecl f) {
exists(GoCfg::ControlFlow::NormalExitNode exit |
exit.getEnclosingCallable() = f and
exists(exit.getAPredecessor())
)
}
/**
* Holds if `pred` is the node for the case `testExpr` in an expression
@@ -352,10 +368,18 @@ module ControlFlow {
predicate isSwitchCaseTestPassingEdge(
ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr
) {
CFG::isSwitchCaseTestPassingEdge(pred, succ, switchExpr, testExpr)
exists(ExpressionSwitchStmt ess, CaseClause cc, int i |
ess.getExpr() = switchExpr and
cc = ess.getACase() and
testExpr = cc.getExpr(i) and
pred.isAfter(testExpr) and
succ.isFirstNodeOf(cc.getStmt(0))
)
}
}
class ControlFlowNode = ControlFlow::Node;
class CfgScope = GoCfg::CfgScope;
class Write = ControlFlow::WriteNode;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -200,7 +200,7 @@ private ControlFlow::Node mostRecentSideEffect(ControlFlow::Node entry, ControlF
cached
private ControlFlow::Node mostRecentSideEffectUnique(ControlFlow::Node node) {
result = unique( | | mostRecentSideEffect(_, node))
result = unique( | | mostRecentSideEffect(getControlFlowEntry(node), node))
}
/** Used to represent the "global value number" of an expression. */

View File

@@ -89,7 +89,7 @@ private module Internal {
/** Holds if the `i`th node of `bb` in function `f` is an entry node. */
private predicate entryNode(FuncDef f, ReachableBasicBlock bb, int i) {
f = bb.getScope() and
bb.getNode(i).isEntryNode()
bb.getNode(i).(ControlFlow::Node).isEntryNode()
}
/**

View File

@@ -59,7 +59,7 @@ module Glog {
/** Holds if this function takes a format string. */
predicate formatter() { format = "f" }
override predicate mayReturnNormally() { level != "Fatal" and level != "Exit" }
override predicate mustNotReturnNormally() { level = "Fatal" or level = "Exit" }
}
private class StringFormatter extends StringOps::Formatting::Range instanceof GlogFunction {

View File

@@ -29,8 +29,8 @@ module Logrus {
)
}
override predicate mayReturnNormally() {
not exists(string level, string suffix | level = ["Fatal", "Panic"] |
override predicate mustNotReturnNormally() {
exists(string level, string suffix | level = ["Fatal", "Panic"] |
this.getName() = level + suffix
)
}

View File

@@ -154,7 +154,7 @@ module Revel {
private IR::EvalInstruction skipImplicitFieldReads(IR::Instruction insn) {
result = insn or
result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBase())
result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBaseInstruction())
}
/** A call to `Controller.Render`. */

View File

@@ -54,7 +54,7 @@ module Zap {
this.hasQualifiedName(packagePath(), "SugaredLogger", "Fatal" + getSuffix())
}
override predicate mayReturnNormally() { none() }
override predicate mustNotReturnNormally() { any() }
}
/** A Zap logging function which always panics. */

View File

@@ -44,7 +44,7 @@ module Log {
)
}
override predicate mayReturnNormally() { none() }
override predicate mustNotReturnNormally() { any() }
}
/** A log function which must panic. */

View File

@@ -12,7 +12,7 @@ module Os {
private class Exit extends Function {
Exit() { this.hasQualifiedName("os", "Exit") }
override predicate mayReturnNormally() { none() }
override predicate mustNotReturnNormally() { any() }
}
// These models are not implemented using Models-as-Data because they represent reverse flow.

View File

@@ -8,23 +8,23 @@
edges
| DivideByZero.go:10:12:10:16 | selection of URL | DivideByZero.go:10:12:10:24 | call to Query | provenance | Src:MaD:1 MaD:2 |
| DivideByZero.go:10:12:10:24 | call to Query | DivideByZero.go:11:27:11:32 | param1 | provenance | |
| DivideByZero.go:11:2:11:33 | ... := ...[0] | DivideByZero.go:12:16:12:20 | value | provenance | |
| DivideByZero.go:11:27:11:32 | param1 | DivideByZero.go:11:2:11:33 | ... := ...[0] | provenance | Config |
| DivideByZero.go:11:2:11:33 | extract:0 ... := ... | DivideByZero.go:12:16:12:20 | value | provenance | |
| DivideByZero.go:11:27:11:32 | param1 | DivideByZero.go:11:2:11:33 | extract:0 ... := ... | provenance | Config |
| DivideByZero.go:17:12:17:16 | selection of URL | DivideByZero.go:17:12:17:24 | call to Query | provenance | Src:MaD:1 MaD:2 |
| DivideByZero.go:17:12:17:24 | call to Query | DivideByZero.go:18:11:18:24 | type conversion | provenance | |
| DivideByZero.go:18:11:18:24 | type conversion | DivideByZero.go:19:16:19:20 | value | provenance | |
| DivideByZero.go:24:12:24:16 | selection of URL | DivideByZero.go:24:12:24:24 | call to Query | provenance | Src:MaD:1 MaD:2 |
| DivideByZero.go:24:12:24:24 | call to Query | DivideByZero.go:25:31:25:36 | param1 | provenance | |
| DivideByZero.go:25:2:25:45 | ... := ...[0] | DivideByZero.go:26:16:26:20 | value | provenance | |
| DivideByZero.go:25:31:25:36 | param1 | DivideByZero.go:25:2:25:45 | ... := ...[0] | provenance | Config |
| DivideByZero.go:25:2:25:45 | extract:0 ... := ... | DivideByZero.go:26:16:26:20 | value | provenance | |
| DivideByZero.go:25:31:25:36 | param1 | DivideByZero.go:25:2:25:45 | extract:0 ... := ... | provenance | Config |
| DivideByZero.go:31:12:31:16 | selection of URL | DivideByZero.go:31:12:31:24 | call to Query | provenance | Src:MaD:1 MaD:2 |
| DivideByZero.go:31:12:31:24 | call to Query | DivideByZero.go:32:33:32:38 | param1 | provenance | |
| DivideByZero.go:32:2:32:43 | ... := ...[0] | DivideByZero.go:33:16:33:20 | value | provenance | |
| DivideByZero.go:32:33:32:38 | param1 | DivideByZero.go:32:2:32:43 | ... := ...[0] | provenance | Config |
| DivideByZero.go:32:2:32:43 | extract:0 ... := ... | DivideByZero.go:33:16:33:20 | value | provenance | |
| DivideByZero.go:32:33:32:38 | param1 | DivideByZero.go:32:2:32:43 | extract:0 ... := ... | provenance | Config |
| DivideByZero.go:38:12:38:16 | selection of URL | DivideByZero.go:38:12:38:24 | call to Query | provenance | Src:MaD:1 MaD:2 |
| DivideByZero.go:38:12:38:24 | call to Query | DivideByZero.go:39:32:39:37 | param1 | provenance | |
| DivideByZero.go:39:2:39:46 | ... := ...[0] | DivideByZero.go:40:16:40:20 | value | provenance | |
| DivideByZero.go:39:32:39:37 | param1 | DivideByZero.go:39:2:39:46 | ... := ...[0] | provenance | Config |
| DivideByZero.go:39:2:39:46 | extract:0 ... := ... | DivideByZero.go:40:16:40:20 | value | provenance | |
| DivideByZero.go:39:32:39:37 | param1 | DivideByZero.go:39:2:39:46 | extract:0 ... := ... | provenance | Config |
| DivideByZero.go:54:12:54:16 | selection of URL | DivideByZero.go:54:12:54:24 | call to Query | provenance | Src:MaD:1 MaD:2 |
| DivideByZero.go:54:12:54:24 | call to Query | DivideByZero.go:55:11:55:24 | type conversion | provenance | |
| DivideByZero.go:55:11:55:24 | type conversion | DivideByZero.go:57:17:57:21 | value | provenance | |
@@ -34,7 +34,7 @@ models
nodes
| DivideByZero.go:10:12:10:16 | selection of URL | semmle.label | selection of URL |
| DivideByZero.go:10:12:10:24 | call to Query | semmle.label | call to Query |
| DivideByZero.go:11:2:11:33 | ... := ...[0] | semmle.label | ... := ...[0] |
| DivideByZero.go:11:2:11:33 | extract:0 ... := ... | semmle.label | extract:0 ... := ... |
| DivideByZero.go:11:27:11:32 | param1 | semmle.label | param1 |
| DivideByZero.go:12:16:12:20 | value | semmle.label | value |
| DivideByZero.go:17:12:17:16 | selection of URL | semmle.label | selection of URL |
@@ -43,17 +43,17 @@ nodes
| DivideByZero.go:19:16:19:20 | value | semmle.label | value |
| DivideByZero.go:24:12:24:16 | selection of URL | semmle.label | selection of URL |
| DivideByZero.go:24:12:24:24 | call to Query | semmle.label | call to Query |
| DivideByZero.go:25:2:25:45 | ... := ...[0] | semmle.label | ... := ...[0] |
| DivideByZero.go:25:2:25:45 | extract:0 ... := ... | semmle.label | extract:0 ... := ... |
| DivideByZero.go:25:31:25:36 | param1 | semmle.label | param1 |
| DivideByZero.go:26:16:26:20 | value | semmle.label | value |
| DivideByZero.go:31:12:31:16 | selection of URL | semmle.label | selection of URL |
| DivideByZero.go:31:12:31:24 | call to Query | semmle.label | call to Query |
| DivideByZero.go:32:2:32:43 | ... := ...[0] | semmle.label | ... := ...[0] |
| DivideByZero.go:32:2:32:43 | extract:0 ... := ... | semmle.label | extract:0 ... := ... |
| DivideByZero.go:32:33:32:38 | param1 | semmle.label | param1 |
| DivideByZero.go:33:16:33:20 | value | semmle.label | value |
| DivideByZero.go:38:12:38:16 | selection of URL | semmle.label | selection of URL |
| DivideByZero.go:38:12:38:24 | call to Query | semmle.label | call to Query |
| DivideByZero.go:39:2:39:46 | ... := ...[0] | semmle.label | ... := ...[0] |
| DivideByZero.go:39:2:39:46 | extract:0 ... := ... | semmle.label | extract:0 ... := ... |
| DivideByZero.go:39:32:39:37 | param1 | semmle.label | param1 |
| DivideByZero.go:40:16:40:20 | value | semmle.label | value |
| DivideByZero.go:54:12:54:16 | selection of URL | semmle.label | selection of URL |

View File

@@ -8,14 +8,14 @@ edges
| Dsn.go:28:102:28:109 | index expression | Dsn.go:28:11:28:110 | []type{args} [array] | provenance | |
| Dsn.go:28:102:28:109 | index expression | Dsn.go:28:11:28:110 | call to Sprintf | provenance | FunctionModel |
| Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | Dsn.go:67:102:67:104 | cfg [pointer] | provenance | |
| Dsn.go:63:9:63:11 | implicit dereference [postupdate] | Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | provenance | |
| Dsn.go:63:9:63:11 | implicit dereference [postupdate] | Dsn.go:67:102:67:108 | selection of dsn | provenance | |
| Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | provenance | |
| Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | Dsn.go:67:102:67:108 | selection of dsn | provenance | |
| Dsn.go:63:19:63:25 | selection of Args | Dsn.go:63:19:63:29 | slice expression | provenance | Src:MaD:1 |
| Dsn.go:63:19:63:29 | slice expression | Dsn.go:63:9:63:11 | implicit dereference [postupdate] | provenance | FunctionModel |
| Dsn.go:63:19:63:29 | slice expression | Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | provenance | FunctionModel |
| Dsn.go:67:11:67:109 | []type{args} [array] | Dsn.go:67:11:67:109 | call to Sprintf | provenance | MaD:2 |
| Dsn.go:67:11:67:109 | call to Sprintf | Dsn.go:68:29:68:33 | dbDSN | provenance | |
| Dsn.go:67:102:67:104 | cfg [pointer] | Dsn.go:67:102:67:104 | implicit dereference | provenance | |
| Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:67:102:67:108 | selection of dsn | provenance | |
| Dsn.go:67:102:67:104 | cfg [pointer] | Dsn.go:67:102:67:104 | implicit-deref cfg | provenance | |
| Dsn.go:67:102:67:104 | implicit-deref cfg | Dsn.go:67:102:67:108 | selection of dsn | provenance | |
| Dsn.go:67:102:67:108 | selection of dsn | Dsn.go:67:11:67:109 | []type{args} [array] | provenance | |
| Dsn.go:67:102:67:108 | selection of dsn | Dsn.go:67:11:67:109 | call to Sprintf | provenance | FunctionModel |
models
@@ -28,13 +28,13 @@ nodes
| Dsn.go:28:102:28:109 | index expression | semmle.label | index expression |
| Dsn.go:29:29:29:33 | dbDSN | semmle.label | dbDSN |
| Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | semmle.label | cfg [postupdate] [pointer] |
| Dsn.go:63:9:63:11 | implicit dereference [postupdate] | semmle.label | implicit dereference [postupdate] |
| Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | semmle.label | implicit-deref cfg [postupdate] |
| Dsn.go:63:19:63:25 | selection of Args | semmle.label | selection of Args |
| Dsn.go:63:19:63:29 | slice expression | semmle.label | slice expression |
| Dsn.go:67:11:67:109 | []type{args} [array] | semmle.label | []type{args} [array] |
| Dsn.go:67:11:67:109 | call to Sprintf | semmle.label | call to Sprintf |
| Dsn.go:67:102:67:104 | cfg [pointer] | semmle.label | cfg [pointer] |
| Dsn.go:67:102:67:104 | implicit dereference | semmle.label | implicit dereference |
| Dsn.go:67:102:67:104 | implicit-deref cfg | semmle.label | implicit-deref cfg |
| Dsn.go:67:102:67:108 | selection of dsn | semmle.label | selection of dsn |
| Dsn.go:68:29:68:33 | dbDSN | semmle.label | dbDSN |
subpaths

View File

@@ -1,9 +1,9 @@
| test.go:9:2:9:16 | ... := ...[0] | test.go:9:13:9:16 | <-... | 0 | file://:0:0:0:0 | bool |
| test.go:9:2:9:16 | ... := ...[1] | test.go:9:13:9:16 | <-... | 1 | file://:0:0:0:0 | bool |
| test.go:15:2:15:20 | ... := ...[0] | test.go:15:13:15:20 | index expression | 0 | file://:0:0:0:0 | string |
| test.go:15:2:15:20 | ... := ...[1] | test.go:15:13:15:20 | index expression | 1 | file://:0:0:0:0 | bool |
| test.go:21:2:21:22 | ... := ...[0] | test.go:21:13:21:22 | type assertion | 0 | file://:0:0:0:0 | string |
| test.go:21:2:21:22 | ... := ...[1] | test.go:21:13:21:22 | type assertion | 1 | file://:0:0:0:0 | bool |
| test.go:9:2:9:16 | extract:0 ... := ... | test.go:9:13:9:16 | <-... | 0 | file://:0:0:0:0 | bool |
| test.go:9:2:9:16 | extract:1 ... := ... | test.go:9:13:9:16 | <-... | 1 | file://:0:0:0:0 | bool |
| test.go:15:2:15:20 | extract:0 ... := ... | test.go:15:13:15:20 | index expression | 0 | file://:0:0:0:0 | string |
| test.go:15:2:15:20 | extract:1 ... := ... | test.go:15:13:15:20 | index expression | 1 | file://:0:0:0:0 | bool |
| test.go:21:2:21:22 | extract:0 ... := ... | test.go:21:13:21:22 | type assertion | 0 | file://:0:0:0:0 | string |
| test.go:21:2:21:22 | extract:1 ... := ... | test.go:21:13:21:22 | type assertion | 1 | file://:0:0:0:0 | bool |
| test.go:29:2:29:7 | call to f[0] | test.go:29:4:29:6 | call to g | 0 | file://:0:0:0:0 | int |
| test.go:29:2:29:7 | call to f[1] | test.go:29:4:29:6 | call to g | 1 | file://:0:0:0:0 | int |
| test.go:33:2:33:7 | call to f[0] | test.go:33:4:33:6 | call to v | 0 | file://:0:0:0:0 | int |

View File

@@ -13,7 +13,7 @@ func logSomething(entry *logrus.Entry) {
entry.Traceln(text) // $ logger=text
}
func logrusCalls() {
func logrusCalls(selector int) {
err := errors.New("Error")
var fields logrus.Fields = nil
var fn logrus.LogFunction = nil
@@ -27,11 +27,15 @@ func logrusCalls() {
tmp = logrus.WithFields(fields) // $ logger=fields
logSomething(tmp)
logrus.Error(text) // $ logger=text
logrus.Fatalf(fmt, text) // $ logger=fmt logger=text
logrus.Panicln(text) // $ logger=text
logrus.Infof(fmt, text) // $ logger=fmt logger=text
logrus.FatalFn(fn) // $ logger=fn
logrus.Error(text) // $ logger=text
logrus.Infof(fmt, text) // $ logger=fmt logger=text
if selector == 0 {
logrus.Fatalf(fmt, text) // $ logger=fmt logger=text
} else if selector == 1 {
logrus.Panicln(text) // $ logger=text
} else if selector == 2 {
logrus.FatalFn(fn) // $ logger=fn
}
// components corresponding to the format specifier "%T" are not considered vulnerable
logrus.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v

View File

@@ -7,5 +7,5 @@ var v []byte
func main() {
glogTest(len(v))
stdlib()
stdlib(len(v))
}

View File

@@ -4,37 +4,69 @@ import (
"log"
)
func stdlib() {
func stdlib(selector int) {
var logger log.Logger
logger.SetPrefix("prefix: ")
logger.Fatal(text) // $ logger=text
logger.Fatalf(fmt, text) // $ logger=fmt logger=text
logger.Fatalln(text) // $ logger=text
logger.Panic(text) // $ logger=text
logger.Panicf(fmt, text) // $ logger=fmt logger=text
logger.Panicln(text) // $ logger=text
logger.Print(text) // $ logger=text
logger.Printf(fmt, text) // $ logger=fmt logger=text
logger.Println(text) // $ logger=text
switch selector {
case 0:
logger.Fatal(text) // $ logger=text
case 1:
logger.Fatalf(fmt, text) // $ logger=fmt logger=text
case 2:
logger.Fatalln(text) // $ logger=text
case 3:
logger.Panic(text) // $ logger=text
case 4:
logger.Panicf(fmt, text) // $ logger=fmt logger=text
case 5:
logger.Panicln(text) // $ logger=text
case 6:
logger.Print(text) // $ logger=text
case 7:
logger.Printf(fmt, text) // $ logger=fmt logger=text
case 8:
logger.Println(text) // $ logger=text
}
// components corresponding to the format specifier "%T" are not considered vulnerable
logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
switch selector {
case 9:
logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 10:
logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 11:
logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
}
log.SetPrefix("prefix: ")
log.Fatal(text) // $ logger=text
log.Fatalf(fmt, text) // $ logger=fmt logger=text
log.Fatalln(text) // $ logger=text
log.Panic(text) // $ logger=text
log.Panicf(fmt, text) // $ logger=fmt logger=text
log.Panicln(text) // $ logger=text
log.Print(text) // $ logger=text
log.Printf(fmt, text) // $ logger=fmt logger=text
log.Println(text) // $ logger=text
switch selector {
case 12:
log.Fatal(text) // $ logger=text
case 13:
log.Fatalf(fmt, text) // $ logger=fmt logger=text
case 14:
log.Fatalln(text) // $ logger=text
case 15:
log.Panic(text) // $ logger=text
case 16:
log.Panicf(fmt, text) // $ logger=fmt logger=text
case 17:
log.Panicln(text) // $ logger=text
case 18:
log.Print(text) // $ logger=text
case 19:
log.Printf(fmt, text) // $ logger=fmt logger=text
case 20:
log.Println(text) // $ logger=text
}
// components corresponding to the format specifier "%T" are not considered vulnerable
log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
switch selector {
case 21:
log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 22:
log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 23:
log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
}
}

View File

@@ -18,4 +18,3 @@
| stmts7.go:10:6:10:15 | canRecover | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.canRecover |
| stmts.go:10:6:10:10 | test5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test5 |
| stmts.go:46:6:46:10 | test6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test6 |
| stmts.go:112:6:112:10 | test9 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test9 |

View File

@@ -9,9 +9,9 @@ import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
import utils.test.InlineFlowTest
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { sourceNode(src, "qltest") }
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
predicate isSink(DataFlow::Node src) { sinkNode(src, "qltest") }
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
}
import ValueFlowTest<Config>

View File

@@ -4,9 +4,9 @@ invalidModelRow
| test.go:40:8:40:15 | call to Src2 | qltest |
| test.go:40:8:40:15 | call to Src2 | qltest-w-subtypes |
| test.go:41:8:41:16 | call to Src2 | qltest-w-subtypes |
| test.go:42:2:42:21 | ... = ...[0] | qltest |
| test.go:42:2:42:21 | ... = ...[1] | qltest-w-subtypes |
| test.go:43:2:43:22 | ... = ...[1] | qltest-w-subtypes |
| test.go:42:2:42:21 | extract:0 ... = ... | qltest |
| test.go:42:2:42:21 | extract:1 ... = ... | qltest-w-subtypes |
| test.go:43:2:43:22 | extract:1 ... = ... | qltest-w-subtypes |
| test.go:44:11:44:13 | arg [postupdate] | qltest-arg |
| test.go:59:9:59:16 | call to Src1 | qltest |
| test.go:102:46:102:53 | call to Src1 | qltest |
@@ -22,4 +22,4 @@ invalidModelRow
| test.go:187:24:187:31 | call to Src1 | qltest |
| test.go:191:24:191:31 | call to Src1 | qltest |
| test.go:209:10:209:28 | selection of SourceVariable | qltest |
| test.go:216:15:216:17 | definition of src | qltest |
| test.go:216:37:218:1 | definition of src | qltest |

View File

@@ -1,14 +1,14 @@
invalidModelRow
#select
| test.go:17:23:17:25 | arg | test.go:17:10:17:26 | call to StepArgRes |
| test.go:18:27:18:29 | arg | test.go:18:2:18:30 | ... = ...[1] |
| test.go:18:27:18:29 | arg | test.go:18:2:18:30 | extract:1 ... = ... |
| test.go:19:15:19:17 | arg | test.go:19:20:19:23 | arg1 [postupdate] |
| test.go:21:16:21:18 | arg | test.go:21:2:21:2 | t [postupdate] |
| test.go:22:10:22:10 | t | test.go:22:10:22:24 | call to StepQualRes |
| test.go:23:2:23:2 | t | test.go:23:16:23:18 | arg [postupdate] |
| test.go:24:32:24:34 | arg | test.go:24:10:24:35 | call to StepArgResNoQual |
| test.go:61:25:61:27 | src | test.go:61:12:61:28 | call to StepArgRes |
| test.go:64:29:64:31 | src | test.go:64:2:64:32 | ... := ...[1] |
| test.go:64:29:64:31 | src | test.go:64:2:64:32 | extract:1 ... := ... |
| test.go:68:15:68:17 | src | test.go:68:20:68:25 | taint3 [postupdate] |
| test.go:76:21:76:23 | src | test.go:76:2:76:7 | taint4 [postupdate] |
| test.go:79:13:79:25 | type assertion | test.go:79:12:79:40 | call to StepQualRes |

View File

@@ -1,4 +1,4 @@
| main.go:6:2:6:5 | 1 | main.go:14:7:14:7 | 1 |
| main.go:6:2:6:5 | implicit-one increment statement | main.go:14:7:14:7 | 1 |
| main.go:10:2:10:2 | definition of x | main.go:10:7:10:7 | 0 |
| main.go:10:7:10:7 | 0 | main.go:10:7:10:7 | 0 |
| main.go:11:6:11:6 | definition of y | main.go:10:7:10:7 | 0 |

View File

@@ -1,3 +1,3 @@
| tst.go:19:10:19:14 | index expression | tst.go:19:10:19:11 | xs | tst.go:19:13:19:13 | 1 |
| tst.go:20:10:20:14 | index expression | tst.go:20:10:20:11 | implicit dereference | tst.go:20:13:20:13 | 1 |
| tst.go:20:10:20:14 | index expression | tst.go:20:10:20:11 | implicit-deref ps | tst.go:20:13:20:13 | 1 |
| tst.go:20:10:20:14 | index expression | tst.go:20:10:20:11 | ps | tst.go:20:13:20:13 | 1 |

View File

@@ -1,4 +1,4 @@
| tst.go:8:8:8:10 | selection of f | tst.go:8:8:8:8 | implicit dereference | tst.go:4:2:4:2 | f |
| tst.go:8:8:8:10 | selection of f | tst.go:8:8:8:8 | implicit-deref t | tst.go:4:2:4:2 | f |
| tst.go:8:8:8:10 | selection of f | tst.go:8:8:8:8 | t | tst.go:4:2:4:2 | f |
| tst.go:13:9:13:11 | selection of f | tst.go:13:9:13:9 | t | tst.go:4:2:4:2 | f |
| tst.go:17:8:17:10 | selection of f | tst.go:17:8:17:8 | x | tst.go:4:2:4:2 | f |

View File

@@ -1,3 +1,3 @@
| tst.go:9:9:9:13 | selection of get | tst.go:9:9:9:9 | implicit dereference | tst.go:12:12:12:14 | get |
| tst.go:9:9:9:13 | selection of get | tst.go:9:9:9:9 | implicit-deref t | tst.go:12:12:12:14 | get |
| tst.go:9:9:9:13 | selection of get | tst.go:9:9:9:9 | t | tst.go:12:12:12:14 | get |
| tst.go:18:2:18:7 | selection of bump | tst.go:18:2:18:2 | x | tst.go:7:13:7:16 | bump |

View File

@@ -1,3 +1,3 @@
| tst.go:19:2:19:6 | assignment to element | tst.go:19:2:19:3 | xs [postupdate] | tst.go:19:5:19:5 | 0 | tst.go:19:10:19:14 | index expression |
| tst.go:20:2:20:6 | assignment to element | tst.go:20:2:20:3 | implicit dereference [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression |
| tst.go:20:2:20:6 | assignment to element | tst.go:20:2:20:3 | ps [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression |
| tst.go:19:2:19:14 | assign:0 ... = ... | tst.go:19:2:19:3 | xs [postupdate] | tst.go:19:5:19:5 | 0 | tst.go:19:10:19:14 | index expression |
| tst.go:20:2:20:14 | assign:0 ... = ... | tst.go:20:2:20:3 | implicit-deref ps [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression |
| tst.go:20:2:20:14 | assign:0 ... = ... | tst.go:20:2:20:3 | ps [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression |

View File

@@ -1,3 +1,3 @@
| tst.go:8:2:8:4 | assignment to field f | tst.go:8:2:8:2 | implicit dereference [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... |
| tst.go:8:2:8:4 | assignment to field f | tst.go:8:2:8:2 | t [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... |
| tst.go:17:2:17:4 | assignment to field f | tst.go:17:2:17:2 | x [postupdate] | tst.go:4:2:4:2 | f | tst.go:17:8:17:14 | ...+... |
| tst.go:8:2:8:14 | assign:0 ... = ... | tst.go:8:2:8:2 | implicit-deref t [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... |
| tst.go:8:2:8:14 | assign:0 ... = ... | tst.go:8:2:8:2 | t [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... |
| tst.go:17:2:17:14 | assign:0 ... = ... | tst.go:17:2:17:2 | x [postupdate] | tst.go:4:2:4:2 | f | tst.go:17:8:17:14 | ...+... |

View File

@@ -0,0 +1,2 @@
reverseRead
| main.go:23:3:23:5 | out | Origin of readStep is missing a PostUpdateNode. |

View File

@@ -4,7 +4,7 @@ func source() string {
return "untrusted data"
}
func sink(string) {
func sink(any) {
}
type A struct {
@@ -19,6 +19,10 @@ func functionWithVarArgsParameter(s ...string) string {
return s[1]
}
func functionWithVarArgsOutParameter(in string, out ...*string) {
*out[0] = in
}
func functionWithSliceOfStructsParameter(s []A) string {
return s[1].f
}
@@ -38,6 +42,12 @@ func main() {
sink(functionWithVarArgsParameter(sSlice...)) // $ hasValueFlow="call to functionWithVarArgsParameter"
sink(functionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to functionWithVarArgsParameter"
var out1 *string
var out2 *string
functionWithVarArgsOutParameter(source(), out1, out2)
sink(out1) // $ MISSING: hasValueFlow="out1"
sink(out2) // $ MISSING: hasValueFlow="out2"
sliceOfStructs := []A{{f: source()}}
sink(sliceOfStructs[0].f) // $ hasValueFlow="selection of f"

View File

@@ -0,0 +1,2 @@
invalidModelRow
testFailures

View File

@@ -0,0 +1,21 @@
extensions:
- addsTo:
pack: codeql/go-all
extensible: summaryModel
data:
- ["github.com/nonexistent/test", "", False, "FunctionWithParameter", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
- ["github.com/nonexistent/test", "", False, "FunctionWithSliceParameter", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsParameter", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsOutParameter", "", "", "Argument[0]", "Argument[1].ArrayElement", "value", "manual"]
- ["github.com/nonexistent/test", "", False, "FunctionWithSliceOfStructsParameter", "", "", "Argument[0].ArrayElement.Field[github.com/nonexistent/test.A.Field]", "ReturnValue", "value", "manual"]
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsOfStructsParameter", "", "", "Argument[0].ArrayElement.Field[github.com/nonexistent/test.A.Field]", "ReturnValue", "value", "manual"]
- addsTo:
pack: codeql/go-all
extensible: sourceModel
data:
- ["github.com/nonexistent/test", "", False, "VariadicSource", "", "", "Argument[0]", "qltest", "manual"]
- addsTo:
pack: codeql/go-all
extensible: sinkModel
data:
- ["github.com/nonexistent/test", "", False, "VariadicSink", "", "", "Argument[0]", "qltest", "manual"]

View File

@@ -0,0 +1,22 @@
import go
import semmle.go.dataflow.ExternalFlow
import ModelValidation
import utils.test.InlineFlowTest
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
sourceNode(source, "qltest")
or
exists(Function fn | fn.hasQualifiedName(_, ["source", "taint"]) |
source = fn.getACall().getResult()
)
}
predicate isSink(DataFlow::Node sink) {
sinkNode(sink, "qltest")
or
exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument())
}
}
import FlowTest<Config, Config>

View File

@@ -0,0 +1,5 @@
module semmle.go.Packages
go 1.25
require github.com/nonexistent/test v0.0.0-20200203000000-0000000000000

View File

@@ -0,0 +1,56 @@
package main
import (
"github.com/nonexistent/test"
)
func source() string {
return "untrusted data"
}
func sink(any) {
}
func main() {
s := source()
sink(test.FunctionWithParameter(s)) // $ hasValueFlow="call to FunctionWithParameter"
stringSlice := []string{source()}
sink(stringSlice[0]) // $ hasValueFlow="index expression"
s0 := ""
s1 := source()
sSlice := []string{s0, s1}
sink(test.FunctionWithParameter(sSlice[1])) // $ hasValueFlow="call to FunctionWithParameter"
sink(test.FunctionWithSliceParameter(sSlice)) // $ hasValueFlow="call to FunctionWithSliceParameter"
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
var out1 *string
var out2 *string
test.FunctionWithVarArgsOutParameter(source(), out1, out2)
sink(out1) // $ MISSING: hasValueFlow="out1"
sink(out2) // $ MISSING: hasValueFlow="out2"
sliceOfStructs := []test.A{{Field: source()}}
sink(sliceOfStructs[0].Field) // $ hasValueFlow="selection of Field"
a0 := test.A{Field: ""}
a1 := test.A{Field: source()}
aSlice := []test.A{a0, a1}
sink(test.FunctionWithSliceOfStructsParameter(aSlice)) // $ hasValueFlow="call to FunctionWithSliceOfStructsParameter"
sink(test.FunctionWithVarArgsOfStructsParameter(aSlice...)) // $ hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
sink(test.FunctionWithVarArgsOfStructsParameter(a0, a1)) // $ hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
var variadicSource string
test.VariadicSource(&variadicSource)
sink(variadicSource) // $ MISSING: hasTaintFlow="variadicSource"
sink(&variadicSource) // $ MISSING: hasTaintFlow="&..."
var variadicSourcePtr *string
test.VariadicSource(variadicSourcePtr)
sink(variadicSourcePtr) // $ MISSING: hasTaintFlow="variadicSourcePtr"
sink(*variadicSourcePtr) // $ MISSING: hasTaintFlow="star expression"
test.VariadicSink(source()) // $ hasTaintFlow="[]type{args}"
}

View File

@@ -0,0 +1,32 @@
package test
type A struct {
Field string
}
func FunctionWithParameter(s string) string {
return ""
}
func FunctionWithSliceParameter(s []string) string {
return ""
}
func FunctionWithVarArgsParameter(s ...string) string {
return ""
}
func FunctionWithVarArgsOutParameter(in string, out ...*string) {
}
func FunctionWithSliceOfStructsParameter(s []A) string {
return ""
}
func FunctionWithVarArgsOfStructsParameter(s ...A) string {
return ""
}
func VariadicSource(s ...*string) {}
func VariadicSink(s ...string) {}

View File

@@ -0,0 +1,3 @@
# github.com/nonexistent/test v0.0.0-20200203000000-0000000000000
## explicit
github.com/nonexistent/test

View File

@@ -20,6 +20,9 @@ class SummaryModelTest extends DataFlow::FunctionModel {
this.hasQualifiedName("github.com/nonexistent/test", "FunctionWithVarArgsParameter") and
(inp.isParameter(_) and outp.isResult())
or
this.hasQualifiedName("github.com/nonexistent/test", "FunctionWithVarArgsOutParameter") and
(inp.isParameter(0) and outp.isParameter(any(int i | i >= 1)))
or
this.hasQualifiedName("github.com/nonexistent/test", "FunctionWithSliceOfStructsParameter") and
(inp.isParameter(0) and outp.isResult())
or

View File

@@ -1,5 +1,5 @@
module semmle.go.Packages
go 1.17
go 1.25
require github.com/nonexistent/test v0.0.0-20200203000000-0000000000000

View File

@@ -8,7 +8,7 @@ func source() string {
return "untrusted data"
}
func sink(string) {
func sink(any) {
}
func main() {
@@ -21,10 +21,17 @@ func main() {
s0 := ""
s1 := source()
sSlice := []string{s0, s1}
sink(test.FunctionWithParameter(sSlice[1])) // $ hasValueFlow="call to FunctionWithParameter"
sink(test.FunctionWithSliceParameter(sSlice)) // $ hasTaintFlow="call to FunctionWithSliceParameter" MISSING: hasValueFlow="call to FunctionWithSliceParameter"
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ hasTaintFlow="call to FunctionWithVarArgsParameter" MISSING: hasValueFlow="call to FunctionWithVarArgsParameter"
sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ MISSING: hasValueFlow="call to FunctionWithVarArgsParameter"
sink(test.FunctionWithParameter(sSlice[1])) // $ hasValueFlow="call to FunctionWithParameter"
sink(test.FunctionWithSliceParameter(sSlice)) // $ hasTaintFlow="call to FunctionWithSliceParameter" MISSING: hasValueFlow="call to FunctionWithSliceParameter"
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ hasTaintFlow="call to FunctionWithVarArgsParameter" MISSING: hasValueFlow="call to FunctionWithVarArgsParameter"
randomFunctionWithMoreThanOneParameter(1, 2, 3, 4, 5) // This is needed to make the next line pass, because we need to have seen a call to a function with at least 2 parameters for ParameterInput to exist with index 1.
sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
var out1 *string
var out2 *string
test.FunctionWithVarArgsOutParameter(source(), out1, out2)
sink(out1) // $ hasValueFlow="out1"
sink(out2) // $ hasValueFlow="out2"
sliceOfStructs := []test.A{{Field: source()}}
sink(sliceOfStructs[0].Field) // $ hasValueFlow="selection of Field"
@@ -37,3 +44,6 @@ func main() {
sink(test.FunctionWithVarArgsOfStructsParameter(aSlice...)) // $ MISSING: hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
sink(test.FunctionWithVarArgsOfStructsParameter(a0, a1)) // $ MISSING: hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
}
func randomFunctionWithMoreThanOneParameter(i1, i2, i3, i4, i5 int) {
}

View File

@@ -16,6 +16,9 @@ func FunctionWithVarArgsParameter(s ...string) string {
return ""
}
func FunctionWithVarArgsOutParameter(in string, out ...*string) {
}
func FunctionWithSliceOfStructsParameter(s []A) string {
return ""
}

View File

@@ -5,11 +5,11 @@
| test.go:34:16:34:20 | param | test.go:33:11:33:27 | call to QueryParams | test.go:34:16:34:20 | param | Cross-site scripting vulnerability due to $@. | test.go:33:11:33:27 | call to QueryParams | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:40:16:40:19 | qstr | test.go:39:10:39:26 | call to QueryString | test.go:40:16:40:19 | qstr | Cross-site scripting vulnerability due to $@. | test.go:39:10:39:26 | call to QueryString | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:46:16:46:18 | val | test.go:45:9:45:34 | call to FormValue | test.go:46:16:46:18 | val | Cross-site scripting vulnerability due to $@. | test.go:45:9:45:34 | call to FormValue | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:52:16:52:37 | index expression | test.go:51:2:51:30 | ... := ...[0] | test.go:52:16:52:37 | index expression | Cross-site scripting vulnerability due to $@. | test.go:51:2:51:30 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:61:20:61:25 | buffer | test.go:57:2:57:46 | ... := ...[0] | test.go:61:20:61:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:57:2:57:46 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:67:16:67:41 | index expression | test.go:66:2:66:31 | ... := ...[0] | test.go:67:16:67:41 | index expression | Cross-site scripting vulnerability due to $@. | test.go:66:2:66:31 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:77:20:77:25 | buffer | test.go:72:2:72:31 | ... := ...[0] | test.go:77:20:77:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:72:2:72:31 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:83:16:83:24 | selection of Value | test.go:82:2:82:32 | ... := ...[0] | test.go:83:16:83:24 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:82:2:82:32 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:52:16:52:37 | index expression | test.go:51:2:51:30 | extract:0 ... := ... | test.go:52:16:52:37 | index expression | Cross-site scripting vulnerability due to $@. | test.go:51:2:51:30 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:61:20:61:25 | buffer | test.go:57:2:57:46 | extract:0 ... := ... | test.go:61:20:61:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:57:2:57:46 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:67:16:67:41 | index expression | test.go:66:2:66:31 | extract:0 ... := ... | test.go:67:16:67:41 | index expression | Cross-site scripting vulnerability due to $@. | test.go:66:2:66:31 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:77:20:77:25 | buffer | test.go:72:2:72:31 | extract:0 ... := ... | test.go:77:20:77:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:72:2:72:31 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:83:16:83:24 | selection of Value | test.go:82:2:82:32 | extract:0 ... := ... | test.go:83:16:83:24 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:82:2:82:32 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:89:16:89:31 | selection of Value | test.go:88:13:88:25 | call to Cookies | test.go:89:16:89:31 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:88:13:88:25 | call to Cookies | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:100:16:100:21 | selection of s | test.go:99:11:99:15 | &... [postupdate] | test.go:100:16:100:21 | selection of s | Cross-site scripting vulnerability due to $@. | test.go:99:11:99:15 | &... [postupdate] | user-provided value | test.go:0:0:0:0 | test.go | |
| test.go:114:16:114:42 | type assertion | test.go:113:21:113:42 | call to Param | test.go:114:16:114:42 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:113:21:113:42 | call to Param | user-provided value | test.go:0:0:0:0 | test.go | |
@@ -25,23 +25,23 @@ edges
| test.go:33:11:33:27 | call to QueryParams | test.go:34:16:34:20 | param | provenance | Src:MaD:11 |
| test.go:39:10:39:26 | call to QueryString | test.go:40:16:40:19 | qstr | provenance | Src:MaD:12 |
| test.go:45:9:45:34 | call to FormValue | test.go:46:16:46:18 | val | provenance | Src:MaD:6 |
| test.go:51:2:51:30 | ... := ...[0] | test.go:52:16:52:37 | index expression | provenance | Src:MaD:5 |
| test.go:57:2:57:46 | ... := ...[0] | test.go:58:13:58:22 | fileHeader | provenance | Src:MaD:4 |
| test.go:58:2:58:29 | ... := ...[0] | test.go:60:2:60:5 | file | provenance | |
| test.go:58:13:58:22 | fileHeader | test.go:58:2:58:29 | ... := ...[0] | provenance | MaD:17 |
| test.go:51:2:51:30 | extract:0 ... := ... | test.go:52:16:52:37 | index expression | provenance | Src:MaD:5 |
| test.go:57:2:57:46 | extract:0 ... := ... | test.go:58:13:58:22 | fileHeader | provenance | Src:MaD:4 |
| test.go:58:2:58:29 | extract:0 ... := ... | test.go:60:2:60:5 | file | provenance | |
| test.go:58:13:58:22 | fileHeader | test.go:58:2:58:29 | extract:0 ... := ... | provenance | MaD:17 |
| test.go:60:2:60:5 | file | test.go:60:12:60:17 | buffer [postupdate] | provenance | MaD:15 |
| test.go:60:2:60:5 | file | test.go:60:12:60:17 | buffer [postupdate] | provenance | MaD:16 |
| test.go:60:2:60:5 | file | test.go:60:12:60:17 | buffer [postupdate] | provenance | MaD:18 |
| test.go:60:12:60:17 | buffer [postupdate] | test.go:61:20:61:25 | buffer | provenance | |
| test.go:66:2:66:31 | ... := ...[0] | test.go:67:16:67:41 | index expression | provenance | Src:MaD:7 |
| test.go:72:2:72:31 | ... := ...[0] | test.go:74:13:74:22 | fileHeader | provenance | Src:MaD:7 |
| test.go:74:2:74:29 | ... := ...[0] | test.go:76:2:76:5 | file | provenance | |
| test.go:74:13:74:22 | fileHeader | test.go:74:2:74:29 | ... := ...[0] | provenance | MaD:17 |
| test.go:66:2:66:31 | extract:0 ... := ... | test.go:67:16:67:41 | index expression | provenance | Src:MaD:7 |
| test.go:72:2:72:31 | extract:0 ... := ... | test.go:74:13:74:22 | fileHeader | provenance | Src:MaD:7 |
| test.go:74:2:74:29 | extract:0 ... := ... | test.go:76:2:76:5 | file | provenance | |
| test.go:74:13:74:22 | fileHeader | test.go:74:2:74:29 | extract:0 ... := ... | provenance | MaD:17 |
| test.go:76:2:76:5 | file | test.go:76:12:76:17 | buffer [postupdate] | provenance | MaD:15 |
| test.go:76:2:76:5 | file | test.go:76:12:76:17 | buffer [postupdate] | provenance | MaD:16 |
| test.go:76:2:76:5 | file | test.go:76:12:76:17 | buffer [postupdate] | provenance | MaD:18 |
| test.go:76:12:76:17 | buffer [postupdate] | test.go:77:20:77:25 | buffer | provenance | |
| test.go:82:2:82:32 | ... := ...[0] | test.go:83:16:83:24 | selection of Value | provenance | Src:MaD:2 |
| test.go:82:2:82:32 | extract:0 ... := ... | test.go:83:16:83:24 | selection of Value | provenance | Src:MaD:2 |
| test.go:88:13:88:25 | call to Cookies | test.go:89:16:89:31 | selection of Value | provenance | Src:MaD:3 |
| test.go:99:11:99:15 | &... [postupdate] | test.go:100:16:100:21 | selection of s | provenance | Src:MaD:1 |
| test.go:113:2:113:4 | ctx [postupdate] | test.go:114:16:114:18 | ctx | provenance | |

View File

@@ -7,18 +7,18 @@
| Gin.go:58:10:58:25 | call to Param |
| Gin.go:62:10:62:34 | call to GetStringSlice |
| Gin.go:66:10:66:29 | call to GetString |
| Gin.go:70:3:70:28 | ... := ...[0] |
| Gin.go:70:3:70:28 | extract:0 ... := ... |
| Gin.go:74:10:74:23 | call to ClientIP |
| Gin.go:78:10:78:26 | call to ContentType |
| Gin.go:82:3:82:29 | ... := ...[0] |
| Gin.go:86:3:86:36 | ... := ...[0] |
| Gin.go:90:3:90:31 | ... := ...[0] |
| Gin.go:94:3:94:39 | ... := ...[0] |
| Gin.go:98:3:98:34 | ... := ...[0] |
| Gin.go:82:3:82:29 | extract:0 ... := ... |
| Gin.go:86:3:86:36 | extract:0 ... := ... |
| Gin.go:90:3:90:31 | extract:0 ... := ... |
| Gin.go:94:3:94:39 | extract:0 ... := ... |
| Gin.go:98:3:98:34 | extract:0 ... := ... |
| Gin.go:102:10:102:52 | call to DefaultPostForm |
| Gin.go:106:10:106:49 | call to DefaultQuery |
| Gin.go:110:3:110:37 | ... := ...[0] |
| Gin.go:114:3:114:34 | ... := ...[0] |
| Gin.go:110:3:110:37 | extract:0 ... := ... |
| Gin.go:114:3:114:34 | extract:0 ... := ... |
| Gin.go:118:10:118:32 | call to GetStringMap |
| Gin.go:122:10:122:38 | call to GetStringMapString |
| Gin.go:126:10:126:43 | call to GetStringMapStringSlice |

View File

@@ -6,11 +6,11 @@ models
| 5 | Source: github.com/emicklei/go-restful; Request; true; ReadEntity; ; ; Argument[0]; remote; manual |
edges
| gorestful.go:15:15:15:44 | call to QueryParameters | gorestful.go:15:15:15:47 | index expression | provenance | Src:MaD:4 Sink:MaD:1 |
| gorestful.go:17:2:17:39 | ... := ...[0] | gorestful.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 |
| gorestful.go:17:2:17:39 | extract:0 ... := ... | gorestful.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 |
| gorestful.go:21:15:21:38 | call to PathParameters | gorestful.go:21:15:21:45 | index expression | provenance | Src:MaD:3 Sink:MaD:1 |
| gorestful.go:23:21:23:24 | &... [postupdate] | gorestful.go:24:15:24:21 | selection of cmd | provenance | Src:MaD:5 Sink:MaD:1 |
| gorestful_v2.go:15:15:15:44 | call to QueryParameters | gorestful_v2.go:15:15:15:47 | index expression | provenance | Src:MaD:4 Sink:MaD:1 |
| gorestful_v2.go:17:2:17:39 | ... := ...[0] | gorestful_v2.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 |
| gorestful_v2.go:17:2:17:39 | extract:0 ... := ... | gorestful_v2.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 |
| gorestful_v2.go:21:15:21:38 | call to PathParameters | gorestful_v2.go:21:15:21:45 | index expression | provenance | Src:MaD:3 Sink:MaD:1 |
| gorestful_v2.go:23:21:23:24 | &... [postupdate] | gorestful_v2.go:24:15:24:21 | selection of cmd | provenance | Src:MaD:5 Sink:MaD:1 |
nodes
@@ -41,14 +41,14 @@ invalidModelRow
#select
| gorestful.go:15:15:15:47 | index expression | gorestful.go:15:15:15:44 | call to QueryParameters | gorestful.go:15:15:15:47 | index expression | This command depends on $@. | gorestful.go:15:15:15:44 | call to QueryParameters | a user-provided value |
| gorestful.go:16:15:16:43 | call to QueryParameter | gorestful.go:16:15:16:43 | call to QueryParameter | gorestful.go:16:15:16:43 | call to QueryParameter | This command depends on $@. | gorestful.go:16:15:16:43 | call to QueryParameter | a user-provided value |
| gorestful.go:18:15:18:17 | val | gorestful.go:17:2:17:39 | ... := ...[0] | gorestful.go:18:15:18:17 | val | This command depends on $@. | gorestful.go:17:2:17:39 | ... := ...[0] | a user-provided value |
| gorestful.go:18:15:18:17 | val | gorestful.go:17:2:17:39 | extract:0 ... := ... | gorestful.go:18:15:18:17 | val | This command depends on $@. | gorestful.go:17:2:17:39 | extract:0 ... := ... | a user-provided value |
| gorestful.go:19:15:19:44 | call to HeaderParameter | gorestful.go:19:15:19:44 | call to HeaderParameter | gorestful.go:19:15:19:44 | call to HeaderParameter | This command depends on $@. | gorestful.go:19:15:19:44 | call to HeaderParameter | a user-provided value |
| gorestful.go:20:15:20:42 | call to PathParameter | gorestful.go:20:15:20:42 | call to PathParameter | gorestful.go:20:15:20:42 | call to PathParameter | This command depends on $@. | gorestful.go:20:15:20:42 | call to PathParameter | a user-provided value |
| gorestful.go:21:15:21:45 | index expression | gorestful.go:21:15:21:38 | call to PathParameters | gorestful.go:21:15:21:45 | index expression | This command depends on $@. | gorestful.go:21:15:21:38 | call to PathParameters | a user-provided value |
| gorestful.go:24:15:24:21 | selection of cmd | gorestful.go:23:21:23:24 | &... [postupdate] | gorestful.go:24:15:24:21 | selection of cmd | This command depends on $@. | gorestful.go:23:21:23:24 | &... [postupdate] | a user-provided value |
| gorestful_v2.go:15:15:15:47 | index expression | gorestful_v2.go:15:15:15:44 | call to QueryParameters | gorestful_v2.go:15:15:15:47 | index expression | This command depends on $@. | gorestful_v2.go:15:15:15:44 | call to QueryParameters | a user-provided value |
| gorestful_v2.go:16:15:16:43 | call to QueryParameter | gorestful_v2.go:16:15:16:43 | call to QueryParameter | gorestful_v2.go:16:15:16:43 | call to QueryParameter | This command depends on $@. | gorestful_v2.go:16:15:16:43 | call to QueryParameter | a user-provided value |
| gorestful_v2.go:18:15:18:17 | val | gorestful_v2.go:17:2:17:39 | ... := ...[0] | gorestful_v2.go:18:15:18:17 | val | This command depends on $@. | gorestful_v2.go:17:2:17:39 | ... := ...[0] | a user-provided value |
| gorestful_v2.go:18:15:18:17 | val | gorestful_v2.go:17:2:17:39 | extract:0 ... := ... | gorestful_v2.go:18:15:18:17 | val | This command depends on $@. | gorestful_v2.go:17:2:17:39 | extract:0 ... := ... | a user-provided value |
| gorestful_v2.go:19:15:19:44 | call to HeaderParameter | gorestful_v2.go:19:15:19:44 | call to HeaderParameter | gorestful_v2.go:19:15:19:44 | call to HeaderParameter | This command depends on $@. | gorestful_v2.go:19:15:19:44 | call to HeaderParameter | a user-provided value |
| gorestful_v2.go:20:15:20:42 | call to PathParameter | gorestful_v2.go:20:15:20:42 | call to PathParameter | gorestful_v2.go:20:15:20:42 | call to PathParameter | This command depends on $@. | gorestful_v2.go:20:15:20:42 | call to PathParameter | a user-provided value |
| gorestful_v2.go:21:15:21:45 | index expression | gorestful_v2.go:21:15:21:38 | call to PathParameters | gorestful_v2.go:21:15:21:45 | index expression | This command depends on $@. | gorestful_v2.go:21:15:21:38 | call to PathParameters | a user-provided value |

View File

@@ -1,11 +1,11 @@
#select
| EndToEnd.go:94:20:94:49 | call to Get | EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:49 | call to Get | This path to an untrusted URL redirection depends on a $@. | EndToEnd.go:94:20:94:27 | selection of Params | user-provided value |
edges
| EndToEnd.go:94:20:94:27 | implicit dereference | EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | provenance | Config |
| EndToEnd.go:94:20:94:27 | implicit dereference | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Config |
| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:27 | implicit dereference | provenance | Src:MaD:2 Config |
| EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | provenance | Config |
| EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Config |
| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | provenance | Src:MaD:2 Config |
| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Src:MaD:2 Config |
| EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | EndToEnd.go:94:20:94:27 | implicit dereference | provenance | Config |
| EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | provenance | Config |
| EndToEnd.go:94:20:94:32 | selection of Form | EndToEnd.go:94:20:94:49 | call to Get | provenance | Config Sink:MaD:1 |
models
| 1 | Sink: group:revel; Controller; true; Redirect; ; ; Argument[0]; url-redirection; manual |

View File

@@ -1,12 +1,12 @@
invalidModelRow
#select
| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | ... := ...[0] |
| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | ... := ...[1] |
| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | ... := ...[0] |
| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | ... := ...[1] |
| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[0] |
| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[1] |
| crypto.go:11:42:11:51 | ciphertext | crypto.go:11:2:11:57 | ... := ...[0] |
| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | extract:0 ... := ... |
| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | extract:1 ... := ... |
| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | extract:0 ... := ... |
| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | extract:1 ... := ... |
| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | extract:0 ... := ... |
| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | extract:1 ... := ... |
| crypto.go:11:42:11:51 | ciphertext | crypto.go:11:2:11:57 | extract:0 ... := ... |
| io.go:14:31:14:43 | "some string" | io.go:14:13:14:44 | call to NewReader |
| io.go:16:23:16:27 | &... | io.go:16:24:16:27 | buf1 [postupdate] |
| io.go:16:23:16:27 | &... [postupdate] | io.go:16:24:16:27 | buf1 [postupdate] |
@@ -31,9 +31,9 @@ invalidModelRow
| io.go:33:20:33:23 | buf1 | io.go:33:19:33:23 | &... |
| io.go:33:20:33:23 | buf1 [postupdate] | io.go:33:19:33:23 | &... |
| io.go:35:16:35:21 | reader | io.go:35:12:35:13 | w2 [postupdate] |
| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | ... := ...[0] |
| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | ... := ...[1] |
| io.go:40:14:40:14 | w [postupdate] | io.go:39:3:39:19 | ... := ...[0] |
| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | extract:0 ... := ... |
| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | extract:1 ... := ... |
| io.go:40:14:40:14 | w [postupdate] | io.go:39:3:39:19 | extract:0 ... := ... |
| io.go:40:17:40:31 | "some string\\n" | io.go:40:14:40:14 | w [postupdate] |
| io.go:43:16:43:16 | r | io.go:43:3:43:5 | buf [postupdate] |
| io.go:44:13:44:15 | buf | io.go:44:13:44:24 | call to String |
@@ -74,35 +74,35 @@ invalidModelRow
| io.go:101:26:101:38 | "some string" | io.go:101:8:101:39 | call to NewReader |
| io.go:102:3:102:3 | r | io.go:102:13:102:21 | selection of Stdout [postupdate] |
| io.go:108:30:108:42 | "some string" | io.go:108:12:108:43 | call to NewReader |
| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | ... := ...[0] |
| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | ... := ...[1] |
| io.go:109:27:109:32 | reader | io.go:109:2:109:33 | ... := ...[0] |
| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | extract:0 ... := ... |
| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | extract:1 ... := ... |
| io.go:109:27:109:32 | reader | io.go:109:2:109:33 | extract:0 ... := ... |
| io.go:110:18:110:20 | buf | io.go:110:2:110:10 | selection of Stdout [postupdate] |
| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | ... := ...[0] |
| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | ... := ...[1] |
| main.go:11:25:11:25 | v | main.go:11:2:11:26 | ... := ...[0] |
| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | ... := ...[0] |
| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | ... := ...[1] |
| main.go:13:33:13:33 | v | main.go:13:2:13:52 | ... := ...[0] |
| main.go:13:36:13:45 | "/*JSON*/" | main.go:13:2:13:52 | ... := ...[0] |
| main.go:13:48:13:51 | " " | main.go:13:2:13:52 | ... := ...[0] |
| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | extract:0 ... := ... |
| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | extract:1 ... := ... |
| main.go:11:25:11:25 | v | main.go:11:2:11:26 | extract:0 ... := ... |
| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | extract:0 ... := ... |
| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | extract:1 ... := ... |
| main.go:13:33:13:33 | v | main.go:13:2:13:52 | extract:0 ... := ... |
| main.go:13:36:13:45 | "/*JSON*/" | main.go:13:2:13:52 | extract:0 ... := ... |
| main.go:13:48:13:51 | " " | main.go:13:2:13:52 | extract:0 ... := ... |
| main.go:14:25:14:25 | b | main.go:14:9:14:41 | slice literal |
| main.go:14:28:14:30 | err | main.go:14:9:14:41 | slice literal |
| main.go:14:33:14:34 | b2 | main.go:14:9:14:41 | slice literal |
| main.go:14:37:14:40 | err2 | main.go:14:9:14:41 | slice literal |
| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | ... := ...[0] |
| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | ... := ...[1] |
| main.go:19:35:19:41 | encoded | main.go:19:2:19:42 | ... := ...[0] |
| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | extract:0 ... := ... |
| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | extract:1 ... := ... |
| main.go:19:35:19:41 | encoded | main.go:19:2:19:42 | extract:0 ... := ... |
| main.go:23:25:23:31 | decoded | main.go:23:9:23:48 | slice literal |
| main.go:23:34:23:36 | err | main.go:23:9:23:48 | slice literal |
| main.go:23:39:23:47 | reEncoded | main.go:23:9:23:48 | slice literal |
| main.go:28:2:28:4 | implicit dereference | main.go:28:2:28:4 | req [postupdate] |
| main.go:28:2:28:4 | implicit dereference | main.go:28:2:28:9 | selection of Body |
| main.go:28:2:28:4 | req | main.go:28:2:28:4 | implicit dereference |
| main.go:28:2:28:4 | req [postupdate] | main.go:28:2:28:4 | implicit dereference |
| main.go:28:2:28:4 | implicit-deref req | main.go:28:2:28:4 | req [postupdate] |
| main.go:28:2:28:4 | implicit-deref req | main.go:28:2:28:9 | selection of Body |
| main.go:28:2:28:4 | req | main.go:28:2:28:4 | implicit-deref req |
| main.go:28:2:28:4 | req [postupdate] | main.go:28:2:28:4 | implicit-deref req |
| main.go:28:2:28:9 | selection of Body | main.go:28:16:28:16 | b [postupdate] |
| main.go:34:2:34:4 | implicit dereference | main.go:34:2:34:4 | req [postupdate] |
| main.go:34:2:34:4 | implicit dereference | main.go:34:2:34:9 | selection of Body |
| main.go:34:2:34:4 | req | main.go:34:2:34:4 | implicit dereference |
| main.go:34:2:34:4 | req [postupdate] | main.go:34:2:34:4 | implicit dereference |
| main.go:34:2:34:4 | implicit-deref req | main.go:34:2:34:4 | req [postupdate] |
| main.go:34:2:34:4 | implicit-deref req | main.go:34:2:34:9 | selection of Body |
| main.go:34:2:34:4 | req | main.go:34:2:34:4 | implicit-deref req |
| main.go:34:2:34:4 | req [postupdate] | main.go:34:2:34:4 | implicit-deref req |
| main.go:34:2:34:9 | selection of Body | main.go:34:16:34:16 | b [postupdate] |

View File

@@ -4,8 +4,8 @@
edges
| client/main.go:16:35:16:78 | &... | server/main.go:19:56:19:61 | definition of params | provenance | |
| client/main.go:16:35:16:78 | &... [postupdate] | client/main.go:16:35:16:78 | &... | provenance | |
| rpc/notes/service.twirp.go:538:2:538:33 | ... := ...[0] | rpc/notes/service.twirp.go:544:27:544:29 | buf | provenance | |
| rpc/notes/service.twirp.go:538:25:538:32 | selection of Body | rpc/notes/service.twirp.go:538:2:538:33 | ... := ...[0] | provenance | Src:MaD:1 MaD:3 |
| rpc/notes/service.twirp.go:538:2:538:33 | extract:0 ... := ... | rpc/notes/service.twirp.go:544:27:544:29 | buf | provenance | |
| rpc/notes/service.twirp.go:538:25:538:32 | selection of Body | rpc/notes/service.twirp.go:538:2:538:33 | extract:0 ... := ... | provenance | Src:MaD:1 MaD:3 |
| rpc/notes/service.twirp.go:544:27:544:29 | buf | rpc/notes/service.twirp.go:544:32:544:41 | reqContent [postupdate] | provenance | MaD:2 |
| rpc/notes/service.twirp.go:544:32:544:41 | reqContent [postupdate] | rpc/notes/service.twirp.go:574:2:577:2 | capture variable reqContent | provenance | |
| rpc/notes/service.twirp.go:574:2:577:2 | capture variable reqContent | rpc/notes/service.twirp.go:576:35:576:44 | reqContent | provenance | |

View File

@@ -1,30 +1,30 @@
#select
| tests.go:10:8:10:8 | f | tests.go:32:5:32:78 | ... := ...[0] | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile |
| tests.go:10:8:10:8 | f | tests.go:46:5:46:76 | ... := ...[0] | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile |
| tests.go:15:3:15:3 | f | tests.go:32:5:32:78 | ... := ...[0] | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile |
| tests.go:15:3:15:3 | f | tests.go:46:5:46:76 | ... := ...[0] | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile |
| tests.go:57:3:57:3 | f | tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:55:15:55:78 | call to OpenFile | call to OpenFile |
| tests.go:69:3:69:3 | f | tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:67:15:67:76 | call to OpenFile | call to OpenFile |
| tests.go:111:9:111:9 | f | tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:109:15:109:78 | call to OpenFile | call to OpenFile |
| tests.go:130:3:130:3 | f | tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:126:15:126:78 | call to OpenFile | call to OpenFile |
| tests.go:151:8:151:8 | f | tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:147:12:147:74 | call to OpenFile | call to OpenFile |
| tests.go:10:8:10:8 | f | tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile |
| tests.go:10:8:10:8 | f | tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile |
| tests.go:15:3:15:3 | f | tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile |
| tests.go:15:3:15:3 | f | tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile |
| tests.go:57:3:57:3 | f | tests.go:55:5:55:78 | extract:0 ... := ... | tests.go:57:3:57:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:55:15:55:78 | call to OpenFile | call to OpenFile |
| tests.go:69:3:69:3 | f | tests.go:67:5:67:76 | extract:0 ... := ... | tests.go:69:3:69:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:67:15:67:76 | call to OpenFile | call to OpenFile |
| tests.go:111:9:111:9 | f | tests.go:109:5:109:78 | extract:0 ... := ... | tests.go:111:9:111:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:109:15:109:78 | call to OpenFile | call to OpenFile |
| tests.go:130:3:130:3 | f | tests.go:126:5:126:78 | extract:0 ... := ... | tests.go:130:3:130:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:126:15:126:78 | call to OpenFile | call to OpenFile |
| tests.go:151:8:151:8 | f | tests.go:147:2:147:74 | extract:0 ... := ... | tests.go:151:8:151:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:147:12:147:74 | call to OpenFile | call to OpenFile |
edges
| tests.go:9:24:9:24 | definition of f | tests.go:10:8:10:8 | f | provenance | |
| tests.go:13:32:13:32 | definition of f | tests.go:14:13:16:2 | capture variable f | provenance | |
| tests.go:14:13:16:2 | capture variable f | tests.go:15:3:15:3 | f | provenance | |
| tests.go:32:5:32:78 | ... := ...[0] | tests.go:33:21:33:21 | f | provenance | Src:MaD:1 |
| tests.go:32:5:32:78 | ... := ...[0] | tests.go:34:29:34:29 | f | provenance | Src:MaD:1 |
| tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:33:21:33:21 | f | provenance | Src:MaD:1 |
| tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:34:29:34:29 | f | provenance | Src:MaD:1 |
| tests.go:33:21:33:21 | f | tests.go:9:24:9:24 | definition of f | provenance | |
| tests.go:34:29:34:29 | f | tests.go:13:32:13:32 | definition of f | provenance | |
| tests.go:46:5:46:76 | ... := ...[0] | tests.go:47:21:47:21 | f | provenance | Src:MaD:1 |
| tests.go:46:5:46:76 | ... := ...[0] | tests.go:48:29:48:29 | f | provenance | Src:MaD:1 |
| tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:47:21:47:21 | f | provenance | Src:MaD:1 |
| tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:48:29:48:29 | f | provenance | Src:MaD:1 |
| tests.go:47:21:47:21 | f | tests.go:9:24:9:24 | definition of f | provenance | |
| tests.go:48:29:48:29 | f | tests.go:13:32:13:32 | definition of f | provenance | |
| tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | provenance | Src:MaD:1 |
| tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | provenance | Src:MaD:1 |
| tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | provenance | Src:MaD:1 |
| tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | provenance | Src:MaD:1 |
| tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | provenance | Src:MaD:1 |
| tests.go:55:5:55:78 | extract:0 ... := ... | tests.go:57:3:57:3 | f | provenance | Src:MaD:1 |
| tests.go:67:5:67:76 | extract:0 ... := ... | tests.go:69:3:69:3 | f | provenance | Src:MaD:1 |
| tests.go:109:5:109:78 | extract:0 ... := ... | tests.go:111:9:111:9 | f | provenance | Src:MaD:1 |
| tests.go:126:5:126:78 | extract:0 ... := ... | tests.go:130:3:130:3 | f | provenance | Src:MaD:1 |
| tests.go:147:2:147:74 | extract:0 ... := ... | tests.go:151:8:151:8 | f | provenance | Src:MaD:1 |
models
| 1 | Source: os; ; false; OpenFile; ; ; ReturnValue[0]; file; manual |
nodes

View File

@@ -1,21 +1,21 @@
#select
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation |
| ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation |
| tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:14:16:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:16:14:16:34 | call to Dir | file system operation |
| tst.go:23:2:43:2 | range statement[1] | tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation |
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation |
| ZipSlip.go:11:2:15:2 | extract:1 range statement | ZipSlip.go:11:2:15:2 | extract:1 range statement | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation |
| tarslip.go:15:2:15:30 | extract:0 ... := ... | tarslip.go:15:2:15:30 | extract:0 ... := ... | tarslip.go:16:14:16:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:16:14:16:34 | call to Dir | file system operation |
| tst.go:23:2:43:2 | extract:1 range statement | tst.go:23:2:43:2 | extract:1 range statement | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation |
edges
| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | provenance | |
| UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | provenance | FunctionModel Sink:MaD:3 |
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | provenance | |
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | provenance | |
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | provenance | |
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | provenance | |
| UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | provenance | |
| UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | provenance | |
| ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:12:24:12:29 | selection of Name | provenance | |
| ZipSlip.go:12:3:12:30 | ... := ...[0] | ZipSlip.go:14:20:14:20 | p | provenance | Sink:MaD:1 |
| ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:3:12:30 | ... := ...[0] | provenance | MaD:4 |
| tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:23:16:33 | selection of Name | provenance | |
| ZipSlip.go:11:2:15:2 | extract:1 range statement | ZipSlip.go:12:24:12:29 | selection of Name | provenance | |
| ZipSlip.go:12:3:12:30 | extract:0 ... := ... | ZipSlip.go:14:20:14:20 | p | provenance | Sink:MaD:1 |
| ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:3:12:30 | extract:0 ... := ... | provenance | MaD:4 |
| tarslip.go:15:2:15:30 | extract:0 ... := ... | tarslip.go:16:23:16:33 | selection of Name | provenance | |
| tarslip.go:16:23:16:33 | selection of Name | tarslip.go:16:14:16:34 | call to Dir | provenance | MaD:5 Sink:MaD:2 |
| tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | provenance | Sink:MaD:1 |
| tst.go:23:2:43:2 | extract:1 range statement | tst.go:29:20:29:23 | path | provenance | Sink:MaD:1 |
models
| 1 | Sink: io/ioutil; ; false; WriteFile; ; ; Argument[0]; path-injection; manual |
| 2 | Sink: os; ; false; MkdirAll; ; ; Argument[0]; path-injection; manual |

View File

@@ -31,16 +31,16 @@ edges
| SqlInjection.go:11:3:11:17 | call to Query | SqlInjection.go:11:3:11:29 | index expression | provenance | |
| SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | []type{args} [array] | provenance | |
| SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | FunctionModel |
| issue48.go:17:2:17:33 | ... := ...[0] | issue48.go:18:17:18:17 | b | provenance | |
| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | ... := ...[0] | provenance | Src:MaD:17 MaD:24 |
| issue48.go:17:2:17:33 | extract:0 ... := ... | issue48.go:18:17:18:17 | b | provenance | |
| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | extract:0 ... := ... | provenance | Src:MaD:17 MaD:24 |
| issue48.go:18:17:18:17 | b | issue48.go:18:20:18:39 | &... [postupdate] | provenance | MaD:22 |
| issue48.go:18:20:18:39 | &... [postupdate] | issue48.go:21:3:21:33 | index expression | provenance | |
| issue48.go:20:8:21:34 | []type{args} [array] | issue48.go:20:8:21:34 | call to Sprintf | provenance | MaD:23 |
| issue48.go:20:8:21:34 | call to Sprintf | issue48.go:22:11:22:12 | q3 | provenance | Sink:MaD:1 |
| issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | []type{args} [array] | provenance | |
| issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | call to Sprintf | provenance | FunctionModel |
| issue48.go:27:2:27:34 | ... := ...[0] | issue48.go:28:17:28:18 | b2 | provenance | |
| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | ... := ...[0] | provenance | Src:MaD:17 MaD:24 |
| issue48.go:27:2:27:34 | extract:0 ... := ... | issue48.go:28:17:28:18 | b2 | provenance | |
| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | extract:0 ... := ... | provenance | Src:MaD:17 MaD:24 |
| issue48.go:28:17:28:18 | b2 | issue48.go:28:21:28:41 | &... [postupdate] | provenance | MaD:22 |
| issue48.go:28:21:28:41 | &... [postupdate] | issue48.go:31:3:31:31 | selection of Category | provenance | |
| issue48.go:30:8:31:32 | []type{args} [array] | issue48.go:30:8:31:32 | call to Sprintf | provenance | MaD:23 |
@@ -72,19 +72,19 @@ edges
| main.go:30:13:30:39 | index expression | main.go:28:18:31:2 | struct literal [Category] | provenance | |
| main.go:33:7:34:23 | []type{args} [array] | main.go:33:7:34:23 | call to Sprintf | provenance | MaD:23 |
| main.go:33:7:34:23 | call to Sprintf | main.go:35:11:35:11 | q | provenance | Sink:MaD:1 |
| main.go:34:3:34:13 | RequestData [pointer, Category] | main.go:34:3:34:13 | implicit dereference [Category] | provenance | |
| main.go:34:3:34:13 | implicit dereference [Category] | main.go:34:3:34:22 | selection of Category | provenance | |
| main.go:34:3:34:13 | RequestData [pointer, Category] | main.go:34:3:34:13 | implicit-deref RequestData [Category] | provenance | |
| main.go:34:3:34:13 | implicit-deref RequestData [Category] | main.go:34:3:34:22 | selection of Category | provenance | |
| main.go:34:3:34:22 | selection of Category | main.go:33:7:34:23 | []type{args} [array] | provenance | |
| main.go:34:3:34:22 | selection of Category | main.go:33:7:34:23 | call to Sprintf | provenance | FunctionModel |
| main.go:40:2:40:12 | RequestData [postupdate] [pointer, Category] | main.go:43:3:43:13 | RequestData [pointer, Category] | provenance | |
| main.go:40:2:40:12 | implicit dereference [postupdate] [Category] | main.go:40:2:40:12 | RequestData [postupdate] [pointer, Category] | provenance | |
| main.go:40:2:40:12 | implicit-deref RequestData [postupdate] [Category] | main.go:40:2:40:12 | RequestData [postupdate] [pointer, Category] | provenance | |
| main.go:40:25:40:31 | selection of URL | main.go:40:25:40:39 | call to Query | provenance | Src:MaD:21 MaD:26 |
| main.go:40:25:40:39 | call to Query | main.go:40:25:40:51 | index expression | provenance | |
| main.go:40:25:40:51 | index expression | main.go:40:2:40:12 | implicit dereference [postupdate] [Category] | provenance | |
| main.go:40:25:40:51 | index expression | main.go:40:2:40:12 | implicit-deref RequestData [postupdate] [Category] | provenance | |
| main.go:42:7:43:23 | []type{args} [array] | main.go:42:7:43:23 | call to Sprintf | provenance | MaD:23 |
| main.go:42:7:43:23 | call to Sprintf | main.go:44:11:44:11 | q | provenance | Sink:MaD:1 |
| main.go:43:3:43:13 | RequestData [pointer, Category] | main.go:43:3:43:13 | implicit dereference [Category] | provenance | |
| main.go:43:3:43:13 | implicit dereference [Category] | main.go:43:3:43:22 | selection of Category | provenance | |
| main.go:43:3:43:13 | RequestData [pointer, Category] | main.go:43:3:43:13 | implicit-deref RequestData [Category] | provenance | |
| main.go:43:3:43:13 | implicit-deref RequestData [Category] | main.go:43:3:43:22 | selection of Category | provenance | |
| main.go:43:3:43:22 | selection of Category | main.go:42:7:43:23 | []type{args} [array] | provenance | |
| main.go:43:3:43:22 | selection of Category | main.go:42:7:43:23 | call to Sprintf | provenance | FunctionModel |
| main.go:49:3:49:14 | star expression [postupdate] [Category] | main.go:49:4:49:14 | RequestData [postupdate] [pointer, Category] | provenance | |

View File

@@ -1,13 +1,13 @@
#select
| StringBreak.go:14:47:14:57 | versionJSON | StringBreak.go:10:2:10:40 | ... := ...[0] | StringBreak.go:14:47:14:57 | versionJSON | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreak.go:10:2:10:40 | ... := ...[0] | JSON value |
| StringBreakMismatched.go:17:26:17:32 | escaped | StringBreakMismatched.go:12:2:12:40 | ... := ...[0] | StringBreakMismatched.go:17:26:17:32 | escaped | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:12:2:12:40 | ... := ...[0] | JSON value |
| StringBreakMismatched.go:29:27:29:33 | escaped | StringBreakMismatched.go:24:2:24:40 | ... := ...[0] | StringBreakMismatched.go:29:27:29:33 | escaped | If this $@ contains a double quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:24:2:24:40 | ... := ...[0] | JSON value |
| StringBreak.go:14:47:14:57 | versionJSON | StringBreak.go:10:2:10:40 | extract:0 ... := ... | StringBreak.go:14:47:14:57 | versionJSON | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreak.go:10:2:10:40 | extract:0 ... := ... | JSON value |
| StringBreakMismatched.go:17:26:17:32 | escaped | StringBreakMismatched.go:12:2:12:40 | extract:0 ... := ... | StringBreakMismatched.go:17:26:17:32 | escaped | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:12:2:12:40 | extract:0 ... := ... | JSON value |
| StringBreakMismatched.go:29:27:29:33 | escaped | StringBreakMismatched.go:24:2:24:40 | extract:0 ... := ... | StringBreakMismatched.go:29:27:29:33 | escaped | If this $@ contains a double quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:24:2:24:40 | extract:0 ... := ... | JSON value |
edges
| StringBreak.go:10:2:10:40 | ... := ...[0] | StringBreak.go:14:47:14:57 | versionJSON | provenance | |
| StringBreakMismatched.go:12:2:12:40 | ... := ...[0] | StringBreakMismatched.go:13:29:13:47 | type conversion | provenance | |
| StringBreak.go:10:2:10:40 | extract:0 ... := ... | StringBreak.go:14:47:14:57 | versionJSON | provenance | |
| StringBreakMismatched.go:12:2:12:40 | extract:0 ... := ... | StringBreakMismatched.go:13:29:13:47 | type conversion | provenance | |
| StringBreakMismatched.go:13:13:13:62 | call to Replace | StringBreakMismatched.go:17:26:17:32 | escaped | provenance | |
| StringBreakMismatched.go:13:29:13:47 | type conversion | StringBreakMismatched.go:13:13:13:62 | call to Replace | provenance | MaD:1 |
| StringBreakMismatched.go:24:2:24:40 | ... := ...[0] | StringBreakMismatched.go:25:29:25:47 | type conversion | provenance | |
| StringBreakMismatched.go:24:2:24:40 | extract:0 ... := ... | StringBreakMismatched.go:25:29:25:47 | type conversion | provenance | |
| StringBreakMismatched.go:25:13:25:61 | call to Replace | StringBreakMismatched.go:29:27:29:33 | escaped | provenance | |
| StringBreakMismatched.go:25:29:25:47 | type conversion | StringBreakMismatched.go:25:13:25:61 | call to Replace | provenance | MaD:1 |
models

View File

@@ -55,10 +55,10 @@
| passwords.go:131:14:131:21 | selection of y | passwords.go:126:13:126:25 | call to getPassword | passwords.go:131:14:131:21 | selection of y | $@ flows to a logging call. | passwords.go:126:13:126:25 | call to getPassword | Sensitive data returned by a call to getPassword |
| protobuf.go:14:14:14:35 | call to GetDescription | protobuf.go:9:2:9:9 | definition of password | protobuf.go:14:14:14:35 | call to GetDescription | $@ flows to a logging call. | protobuf.go:9:2:9:9 | definition of password | Sensitive data returned by an access to password |
edges
| klog.go:21:3:26:3 | range statement[1] | klog.go:22:27:22:33 | headers | provenance | |
| klog.go:21:30:21:37 | selection of Header | klog.go:21:3:26:3 | range statement[1] | provenance | Src:MaD:11 Config |
| klog.go:22:4:25:4 | range statement[1] | klog.go:23:15:23:20 | header | provenance | |
| klog.go:22:27:22:33 | headers | klog.go:22:4:25:4 | range statement[1] | provenance | Config |
| klog.go:21:3:26:3 | extract:1 range statement | klog.go:22:27:22:33 | headers | provenance | |
| klog.go:21:30:21:37 | selection of Header | klog.go:21:3:26:3 | extract:1 range statement | provenance | Src:MaD:11 Config |
| klog.go:22:4:25:4 | extract:1 range statement | klog.go:23:15:23:20 | header | provenance | |
| klog.go:22:27:22:33 | headers | klog.go:22:4:25:4 | extract:1 range statement | provenance | Config |
| klog.go:29:13:29:20 | selection of Header | klog.go:29:13:29:41 | call to Get | provenance | Src:MaD:11 Config |
| main.go:17:2:17:9 | definition of password | main.go:19:12:19:19 | password | provenance | |
| main.go:17:2:17:9 | definition of password | main.go:20:19:20:26 | password | provenance | |
@@ -143,14 +143,14 @@ edges
| passwords.go:130:14:130:19 | config [x] | passwords.go:130:14:130:21 | selection of x | provenance | |
| passwords.go:131:14:131:19 | config [y] | passwords.go:131:14:131:21 | selection of y | provenance | |
| protobuf.go:9:2:9:9 | definition of password | protobuf.go:12:22:12:29 | password | provenance | |
| protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | provenance | |
| protobuf.go:12:2:12:6 | implicit-deref query [postupdate] [Description] | protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | provenance | |
| protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | protobuf.go:14:14:14:18 | query [pointer, Description] | provenance | |
| protobuf.go:12:22:12:29 | password | protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | provenance | |
| protobuf.go:12:22:12:29 | password | protobuf.go:12:2:12:6 | implicit-deref query [postupdate] [Description] | provenance | |
| protobuf.go:14:14:14:18 | query [pointer, Description] | protobuf.go:14:14:14:35 | call to GetDescription | provenance | |
| protobuf.go:14:14:14:18 | query [pointer, Description] | protos/query/query.pb.go:117:7:117:7 | definition of x [pointer, Description] | provenance | |
| protos/query/query.pb.go:117:7:117:7 | definition of x [pointer, Description] | protos/query/query.pb.go:119:10:119:10 | x [pointer, Description] | provenance | |
| protos/query/query.pb.go:119:10:119:10 | implicit dereference [Description] | protos/query/query.pb.go:119:10:119:22 | selection of Description | provenance | |
| protos/query/query.pb.go:119:10:119:10 | x [pointer, Description] | protos/query/query.pb.go:119:10:119:10 | implicit dereference [Description] | provenance | |
| protos/query/query.pb.go:119:10:119:10 | implicit-deref x [Description] | protos/query/query.pb.go:119:10:119:22 | selection of Description | provenance | |
| protos/query/query.pb.go:119:10:119:10 | x [pointer, Description] | protos/query/query.pb.go:119:10:119:10 | implicit-deref x [Description] | provenance | |
models
| 1 | Sink: group:logrus; ; false; WithField; ; ; Argument[0..1]; log-injection; manual |
| 2 | Sink: group:logrus; ; false; WithFields; ; ; Argument[0]; log-injection; manual |

View File

@@ -1,29 +1,29 @@
| encryption.go:30:2:30:36 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:34:2:34:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:38:2:38:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:42:2:42:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:46:2:46:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:50:2:50:47 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:54:2:54:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:56:22:56:91 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:59:21:59:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:59:22:59:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:59:22:59:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:60:10:60:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:65:2:65:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:69:2:69:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
| encryption.go:76:2:76:32 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:80:2:80:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:84:2:84:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:88:2:88:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:92:2:92:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:96:2:96:43 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:100:2:100:41 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:102:22:102:87 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:105:21:105:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:105:22:105:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:105:22:105:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:106:10:106:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:111:2:111:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:115:2:115:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
| encryption.go:30:2:30:36 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:34:2:34:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:38:2:38:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:42:2:42:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:46:2:46:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:50:2:50:47 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:54:2:54:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:56:22:56:91 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:59:21:59:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:59:22:59:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:59:22:59:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:60:10:60:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:65:2:65:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:69:2:69:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES |
| encryption.go:76:2:76:32 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:80:2:80:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:84:2:84:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:88:2:88:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:92:2:92:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:96:2:96:43 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:100:2:100:41 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:102:22:102:87 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:105:21:105:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:105:22:105:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:105:22:105:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:106:10:106:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:111:2:111:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:115:2:115:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES |
| encryption.go:166:2:166:33 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:166:2:166:33 | call to XORKeyStream | The cryptographic algorithm RC4 |

View File

@@ -7,8 +7,8 @@ edges
| go-jose.v3.go:25:16:25:47 | call to Get | go-jose.v3.go:26:15:26:25 | signedToken | provenance | |
| go-jose.v3.go:26:15:26:25 | signedToken | go-jose.v3.go:29:19:29:29 | definition of signedToken | provenance | |
| go-jose.v3.go:29:19:29:29 | definition of signedToken | go-jose.v3.go:31:37:31:47 | signedToken | provenance | |
| go-jose.v3.go:31:2:31:48 | ... := ...[0] | go-jose.v3.go:33:12:33:23 | DecodedToken | provenance | Sink:MaD:2 |
| go-jose.v3.go:31:37:31:47 | signedToken | go-jose.v3.go:31:2:31:48 | ... := ...[0] | provenance | MaD:4 |
| go-jose.v3.go:31:2:31:48 | extract:0 ... := ... | go-jose.v3.go:33:12:33:23 | DecodedToken | provenance | Sink:MaD:2 |
| go-jose.v3.go:31:37:31:47 | signedToken | go-jose.v3.go:31:2:31:48 | extract:0 ... := ... | provenance | MaD:4 |
| golang-jwt-v5.go:28:16:28:20 | selection of URL | golang-jwt-v5.go:28:16:28:28 | call to Query | provenance | Src:MaD:3 MaD:5 |
| golang-jwt-v5.go:28:16:28:28 | call to Query | golang-jwt-v5.go:28:16:28:47 | call to Get | provenance | MaD:6 |
| golang-jwt-v5.go:28:16:28:47 | call to Get | golang-jwt-v5.go:29:25:29:35 | signedToken | provenance | |

View File

@@ -5,8 +5,8 @@ edges
| UncontrolledAllocationSizeBad.go:11:12:11:24 | call to Query | UncontrolledAllocationSizeBad.go:13:15:13:20 | source | provenance | |
| UncontrolledAllocationSizeBad.go:13:15:13:20 | source | UncontrolledAllocationSizeBad.go:13:15:13:29 | call to Get | provenance | MaD:3 |
| UncontrolledAllocationSizeBad.go:13:15:13:29 | call to Get | UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | provenance | |
| UncontrolledAllocationSizeBad.go:14:2:14:37 | ... := ...[0] | UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | provenance | |
| UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | UncontrolledAllocationSizeBad.go:14:2:14:37 | ... := ...[0] | provenance | Config |
| UncontrolledAllocationSizeBad.go:14:2:14:37 | extract:0 ... := ... | UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | provenance | |
| UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | UncontrolledAllocationSizeBad.go:14:2:14:37 | extract:0 ... := ... | provenance | Config |
models
| 1 | Source: net/http; Request; true; URL; ; ; ; remote; manual |
| 2 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual |

View File

@@ -37,9 +37,9 @@ edges
| tst.go:11:13:11:35 | call to FormValue | tst.go:39:11:39:29 | ...+... | provenance | Src:MaD:1 |
| tst.go:11:13:11:35 | call to FormValue | tst.go:41:11:41:40 | ...+... | provenance | Src:MaD:1 |
| tst.go:11:13:11:35 | call to FormValue | tst.go:48:11:48:18 | tainted2 | provenance | Src:MaD:1 |
| tst.go:48:2:48:2 | implicit dereference [postupdate] | tst.go:48:2:48:2 | u [postupdate] | provenance | |
| tst.go:48:2:48:2 | implicit-deref u [postupdate] | tst.go:48:2:48:2 | u [postupdate] | provenance | |
| tst.go:48:2:48:2 | u [postupdate] | tst.go:49:11:49:11 | u | provenance | |
| tst.go:48:11:48:18 | tainted2 | tst.go:48:2:48:2 | implicit dereference [postupdate] | provenance | Config |
| tst.go:48:11:48:18 | tainted2 | tst.go:48:2:48:2 | implicit-deref u [postupdate] | provenance | Config |
| tst.go:48:11:48:18 | tainted2 | tst.go:48:2:48:2 | u [postupdate] | provenance | Config |
| tst.go:49:11:49:11 | u | tst.go:49:11:49:20 | call to String | provenance | MaD:3 |
| websocket.go:60:21:60:31 | call to Referer | websocket.go:65:27:65:40 | untrustedInput | provenance | Src:MaD:2 |

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Improved modeling of Apache HttpClient `execute` method sinks for `java/ssrf` and `java/non-https-url`.

View File

@@ -11,7 +11,7 @@ extensions:
- ["org.apache.http.client.methods", "HttpPost", False, "HttpPost", "", "", "Argument[0]", "request-forgery", "manual"]
- ["org.apache.http.client.methods", "HttpPut", False, "HttpPut", "", "", "Argument[0]", "request-forgery", "manual"]
- ["org.apache.http.client.methods", "HttpRequestBase", True, "setURI", "", "", "Argument[0]", "request-forgery", "manual"]
- ["org.apache.http.client.methods", "HttpRequestWrapper", True, "setURI", "(URI)", "", "Argument[0]", "request-forgery", "hq-manual"]
- ["org.apache.http.client.methods", "HttpRequestWrapper", True, "setURI", "(URI)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client.methods", "HttpTrace", False, "HttpTrace", "", "", "Argument[0]", "request-forgery", "manual"]
- ["org.apache.http.client.methods", "RequestBuilder", False, "delete", "", "", "Argument[0]", "request-forgery", "manual"]
- ["org.apache.http.client.methods", "RequestBuilder", False, "get", "", "", "Argument[0]", "request-forgery", "manual"]
@@ -22,3 +22,29 @@ extensions:
- ["org.apache.http.client.methods", "RequestBuilder", False, "put", "", "", "Argument[0]", "request-forgery", "manual"]
- ["org.apache.http.client.methods", "RequestBuilder", False, "setUri", "", "", "Argument[0]", "request-forgery", "manual"]
- ["org.apache.http.client.methods", "RequestBuilder", False, "trace", "", "", "Argument[0]", "request-forgery", "manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["org.apache.http.client.methods", "RequestBuilder", True, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "delete", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "delete", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "get", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "get", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "getUri", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "head", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "head", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "options", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "options", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "patch", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "patch", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "post", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "post", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "put", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "put", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(String)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(URI)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "trace", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.apache.http.client.methods", "RequestBuilder", True, "trace", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]

View File

@@ -3,6 +3,11 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,ResponseHandler)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]

View File

@@ -0,0 +1,45 @@
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.HttpContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ApacheHttpClientExecuteSSRF extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
String source = request.getParameter("host"); // $ Source
HttpHost host = new HttpHost(source);
HttpRequest req = new BasicHttpRequest("GET", "/");
HttpUriRequest uriReq = RequestBuilder.get(source).build(); // $ Alert
HttpContext context = null;
HttpClient client = HttpClients.createDefault();
ResponseHandler<Object> handler = null;
client.execute(host, req); // $ Alert
client.execute(host, req, context); // $ Alert
client.execute(host, req, handler); // $ Alert
client.execute(host, req, handler, context); // $ Alert
client.execute(uriReq); // $ Alert
client.execute(uriReq, context); // $ Alert
client.execute(uriReq, handler); // $ Alert
client.execute(uriReq, handler, context); // $ Alert
} catch (Exception e) {
// TODO: handle exception
}
}
}

View File

@@ -1 +1 @@
//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/javax-validation-constraints:${testdir}/../../../stubs/springframework-5.8.x:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/projectreactor-3.4.3/:${testdir}/../../../stubs/postgresql-42.3.3/:${testdir}/../../../stubs/HikariCP-3.4.5/:${testdir}/../../../stubs/spring-jdbc-5.3.8/:${testdir}/../../../stubs/jdbi3-core-3.27.2/:${testdir}/../../../stubs/cargo:${testdir}/../../../stubs/javafx-web:${testdir}/../../../stubs/apache-commons-jelly-1.0.1:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/stapler-1.263:${testdir}/../../../stubs/javax-servlet-2.5:${testdir}/../../../stubs/apache-commons-fileupload-1.4:${testdir}/../../../stubs/saxon-xqj-9.x:${testdir}/../../../stubs/apache-commons-beanutils:${testdir}/../../../stubs/apache-commons-lang:${testdir}/../../../stubs/apache-http-5:${testdir}/../../../stubs/playframework-2.6.x:${testdir}/../../../stubs/jaxws-api-2.0:${testdir}/../../../stubs/apache-cxf
//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/javax-validation-constraints:${testdir}/../../../stubs/springframework-5.8.x:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/apache-http-client-4.4.13:${testdir}/../../../stubs/projectreactor-3.4.3/:${testdir}/../../../stubs/postgresql-42.3.3/:${testdir}/../../../stubs/HikariCP-3.4.5/:${testdir}/../../../stubs/spring-jdbc-5.3.8/:${testdir}/../../../stubs/jdbi3-core-3.27.2/:${testdir}/../../../stubs/cargo:${testdir}/../../../stubs/javafx-web:${testdir}/../../../stubs/apache-commons-jelly-1.0.1:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/stapler-1.263:${testdir}/../../../stubs/javax-servlet-2.5:${testdir}/../../../stubs/apache-commons-fileupload-1.4:${testdir}/../../../stubs/saxon-xqj-9.x:${testdir}/../../../stubs/apache-commons-beanutils:${testdir}/../../../stubs/apache-commons-lang:${testdir}/../../../stubs/apache-http-5:${testdir}/../../../stubs/playframework-2.6.x:${testdir}/../../../stubs/jaxws-api-2.0:${testdir}/../../../stubs/apache-cxf

View File

@@ -0,0 +1,23 @@
// Generated automatically from org.apache.http.client.HttpClient for testing purposes
package org.apache.http.client;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.protocol.HttpContext;
public interface HttpClient {
HttpResponse execute(HttpHost target, HttpRequest request) throws IOException;
HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException;
<T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler) throws IOException;
<T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
throws IOException;
HttpResponse execute(HttpUriRequest request) throws IOException;
HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException;
<T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException;
<T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
throws IOException;
}

View File

@@ -0,0 +1,9 @@
// Generated automatically from org.apache.http.client.ResponseHandler for testing purposes
package org.apache.http.client;
import org.apache.http.HttpResponse;
public interface ResponseHandler<T> {
T handleResponse(HttpResponse response);
}

View File

@@ -0,0 +1,7 @@
package org.apache.http.impl.client;
import org.apache.http.client.HttpClient;
public abstract class CloseableHttpClient implements HttpClient {
}

View File

@@ -0,0 +1,10 @@
// Generated automatically from org.apache.http.client.HttpClient for testing purposes
package org.apache.http.impl.client;
import java.io.IOException;
import org.apache.http.impl.client.CloseableHttpClient;
public final class HttpClients {
public static CloseableHttpClient createDefault() { return null; }
}

View File

@@ -21,13 +21,19 @@ file_coverage_languages:
scc_languages:
- TypeScript
- TypeScript Typings
- name: vue
display_name: Vue.js component
scc_languages:
- Vue
github_api_languages:
- JavaScript
- TypeScript
- Vue
scc_languages:
- JavaScript
- TypeScript
- TypeScript Typings
- Vue
file_types:
- name: javascript
display_name: JavaScript

View File

@@ -1,2 +0,0 @@
import semmle.python.controlflow.internal.AstNodeImpl
import ControlFlow::Consistency

View File

@@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
private import semmle.python.dataflow.new.internal.DataFlowDispatch
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
private import codeql.dataflow.internal.DataFlowImplConsistency
private import semmle.python.controlflow.internal.Cfg as Cfg
private module Input implements InputSig<Location, PythonDataFlow> {
private import Private
@@ -73,7 +72,7 @@ private module Input implements InputSig<Location, PythonDataFlow> {
// resolve to multiple functions), but we only make _one_ ArgumentNode for each
// argument in the CallNode, we end up violating this consistency check in those
// cases. (see `getCallArg` in DataFlowDispatch.qll)
exists(DataFlowCall other, Cfg::CallNode cfgCall | other != call |
exists(DataFlowCall other, CallNode cfgCall | other != call |
call.getNode() = cfgCall and
other.getNode() = cfgCall and
isArgumentNode(arg, call, _) and
@@ -89,16 +88,16 @@ private module Input implements InputSig<Location, PythonDataFlow> {
// allow it instead.
(
call.getScope() = attr.getScope() and
any(CfgNode n | n.asCfgNode() = call.getNode().(Cfg::CallNode).getFunction())
.getALocalSource() = attr
any(CfgNode n | n.asCfgNode() = call.getNode().(CallNode).getFunction()).getALocalSource() =
attr
or
not exists(call.getScope().(Function).getDefinition()) and
call.getScope().getScope+() = attr.getScope()
) and
(
other.getScope() = attr.getScope() and
any(CfgNode n | n.asCfgNode() = other.getNode().(Cfg::CallNode).getFunction())
.getALocalSource() = attr
any(CfgNode n | n.asCfgNode() = other.getNode().(CallNode).getFunction()).getALocalSource() =
attr
or
not exists(other.getScope().(Function).getDefinition()) and
other.getScope().getScope+() = attr.getScope()

View File

@@ -213,11 +213,9 @@ class ExprWithPointsTo extends Expr {
* Gets what this expression might "refer-to" in the given `context`.
*/
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
)
this.getAFlowNode()
.(ControlFlowNodeWithPointsTo)
.refersTo(context, obj, cls, origin.getAFlowNode())
}
/**
@@ -228,11 +226,7 @@ class ExprWithPointsTo extends Expr {
*/
pragma[nomagic]
predicate refersTo(Object obj, AstNode origin) {
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
)
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
}
/**
@@ -246,22 +240,16 @@ class ExprWithPointsTo extends Expr {
* in the given `context`.
*/
predicate pointsTo(Context context, Value value, AstNode origin) {
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(context, value, origin_)
)
this.getAFlowNode()
.(ControlFlowNodeWithPointsTo)
.pointsTo(context, value, origin.getAFlowNode())
}
/**
* Holds if this expression might "point-to" to `value` which is from `origin`.
*/
predicate pointsTo(Value value, AstNode origin) {
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
)
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
}
/**
@@ -487,10 +475,7 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics {
not non_coupling_method(result) and
exists(Call call | call.getScope() = this |
exists(FunctionObject callee | callee.getFunction() = result |
exists(CallNode call_ |
call_.getNode() = call and
call_.getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
)
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
)
or
exists(Attribute a | call.getFunc() = a |

View File

@@ -64,7 +64,7 @@ private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
private predicate preferred_jump_to_defn(Expr use, Definition def) {
not use instanceof ClassExpr and
not use instanceof FunctionExpr and
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def))
jump_to_defn(use.getAFlowNode(), def)
}
private predicate unique_jump_to_defn(Expr use, Definition def) {
@@ -452,7 +452,7 @@ private predicate self_parameter_jump_to_defn_attribute(
* This exists primarily for testing use `getPreferredDefinition()` instead.
*/
Definition getADefinition(Expr use) {
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and
jump_to_defn(use.getAFlowNode(), result) and
not use instanceof Call and
not use.isArtificial() and
// Not the use itself

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* A new Python control flow graph implementation has been added under `semmle.python.controlflow.internal.Cfg` (backed by `AstNodeImpl.qll`), built on the shared `codeql.controlflow.ControlFlowGraph` library. It is not yet used by the dataflow library or any production query; the legacy CFG in `semmle/python/Flow.qll` remains the default. The new library is exposed for tests and for upcoming migrations.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* A new SSA adapter has been added under `semmle.python.dataflow.new.internal.SsaImpl`, built on the shared `codeql.ssa.Ssa` library and the new shared CFG (`semmle.python.controlflow.internal.Cfg`). It is not yet used by the dataflow library or any production query; the legacy ESSA SSA in `semmle/python/essa/*` remains the default. The new SSA adapter is exposed for tests and for the upcoming dataflow migration.

View File

@@ -1,5 +0,0 @@
---
category: deprecated
---
* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged.

View File

@@ -1,4 +0,0 @@
---
category: deprecated
---
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
* The deprecated `AstNode.getAFlowNode()` and `Function.getAReturnValueFlowNode()` predicates now return nodes from the new shared CFG (`Cfg::ControlFlowNode`) rather than from the legacy CFG (`ControlFlowNode`). Callers that still rely on these deprecated APIs and feed the result into legacy-CFG-aware predicates will no longer type-check; migrate to `n.getNode() = e` (or, for return values, the explicit `Return` pattern shown in the deprecation message) to get nodes from the dataflow library's current CFG.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The new (shared-CFG-based) Python control flow graph now visits parameter and return type annotations as CFG nodes for function definitions, matching the legacy CFG. This restores annotation-based type tracking through framework models such as FastAPI's `Depends()`, Pydantic request models, Starlette `WebSocket` handlers, and any other models that flow a class reference through `Parameter.getAnnotation()` to identify instances of the annotated class.

View File

@@ -1,45 +0,0 @@
/**
* @name Print CFG (New)
* @description Produces a representation of a file's Control Flow Graph
* using the new shared control flow library.
* This query is used by the VS Code extension.
* @id python/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
*/
private import python as Py
import semmle.python.controlflow.internal.AstNodeImpl
external string selectedSourceFile();
private predicate selectedSourceFileAlias = selectedSourceFile/0;
external int selectedSourceLine();
private predicate selectedSourceLineAlias = selectedSourceLine/0;
external int selectedSourceColumn();
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig<Py::File> {
predicate selectedSourceFile = selectedSourceFileAlias/0;
predicate selectedSourceLine = selectedSourceLineAlias/0;
predicate selectedSourceColumn = selectedSourceColumnAlias/0;
predicate cfgScopeSpan(
Ast::Callable callable, Py::File file, int startLine, int startColumn, int endLine,
int endColumn
) {
exists(Py::Scope scope |
scope = callable.asScope() and
file = scope.getLocation().getFile() and
scope.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn)
)
}
}
import ControlFlow::ViewCfgQuery<Py::File, ViewCfgQueryInput>

View File

@@ -6,9 +6,8 @@
* directed and labeled; they specify how the components represented by nodes relate to each other.
*/
// Importing python under the `PY` namespace to avoid pulling in `CallNode` from `Flow.qll` (via `import python`) and thereby having a naming conflict with `API::CallNode`.
// Importing python under the `py` namespace to avoid importing `CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
private import python as PY
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.DataFlow
private import semmle.python.internal.CachedStages
@@ -283,7 +282,7 @@ module API {
index = this.getIndex() and
(
// subscripting
exists(Cfg::SubscriptNode subscript |
exists(PY::SubscriptNode subscript |
subscript.getObject() = this.getAValueReachableFromSource().asCfgNode() and
subscript.getIndex() = index.asSink().asCfgNode()
|
@@ -291,7 +290,7 @@ module API {
subscript = result.asSource().asCfgNode()
or
// writing
subscript.(Cfg::DefinitionNode).getValue() = result.asSink().asCfgNode()
subscript.(PY::DefinitionNode).getValue() = result.asSink().asCfgNode()
)
or
// dictionary literals
@@ -685,7 +684,7 @@ module API {
* Ignores relative imports, such as `from ..foo.bar import baz`.
*/
private predicate imports(DataFlow::CfgNode imp, string name) {
exists(Cfg::ImportExprNode iexpr |
exists(PY::ImportExprNode iexpr |
imp.getNode() = iexpr and
not iexpr.getNode().isRelative() and
name = iexpr.getNode().getImportedModuleName()
@@ -776,7 +775,7 @@ module API {
// list literals, from `x` to `[x]`
// TODO: once convenient, this should be done at a higher level than the AST,
// at least at the CFG layer, to take splitting into account.
// Also consider `Cfg::SequenceNode` for generality.
// Also consider `SequenceNode for generality.
exists(PY::List list | list = pred.(DataFlow::ExprNode).getNode().getNode() |
rhs.(DataFlow::ExprNode).getNode().getNode() = list.getAnElt() and
lbl = Label::subscript()
@@ -806,7 +805,7 @@ module API {
subscript = trackUseNode(src).getSubscript(index)
|
// from `x` to a definition of `x[...]`
rhs.asCfgNode() = subscript.asCfgNode().(Cfg::DefinitionNode).getValue() and
rhs.asCfgNode() = subscript.asCfgNode().(PY::DefinitionNode).getValue() and
lbl = Label::subscript()
or
// from `x` to `"key"` in `x["key"]`

View File

@@ -3,7 +3,6 @@ module;
import python
private import semmle.python.internal.CachedStages
private import semmle.python.controlflow.internal.Cfg as Cfg
/** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
abstract class AstNode extends AstNode_ {
@@ -17,25 +16,21 @@ abstract class AstNode extends AstNode_ {
/** Gets the scope that this node occurs in */
abstract Scope getScope();
/**
* Gets a flow node corresponding directly to this node.
* NOTE: For some statements and other purely syntactic elements,
* there may not be a `ControlFlowNode`
*/
cached
ControlFlowNode getAFlowNode() {
Stages::AST::ref() and
py_flow_bb_node(result, this, _, _)
}
/** Gets the location for this AST node */
cached
Location getLocation() { none() }
/**
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
* being removed to untangle the AST and CFG hierarchies.
*
* Gets a flow node corresponding directly to this node, from the new
* (shared) CFG. NOTE: For some statements and other purely syntactic
* elements, there may not be a `ControlFlowNode`.
*/
cached
deprecated Cfg::ControlFlowNode getAFlowNode() {
Stages::AST::ref() and
result.getNode() = this
}
/**
* Whether this syntactic element is artificial, that is it is generated
* by the compiler and is not present in the source

View File

@@ -5,7 +5,6 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
private import semmle.python.dataflow.new.RemoteFlowSources
@@ -215,7 +214,7 @@ module Path {
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
}
private predicate safeAccessCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
private predicate safeAccessCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
g.(SafeAccessCheck::Range).checks(node, branch)
}
@@ -224,7 +223,7 @@ module Path {
/** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */
abstract class Range extends DataFlow::GuardNode {
/** Holds if this guard validates `node` upon evaluating to `branch`. */
abstract predicate checks(Cfg::ControlFlowNode node, boolean branch);
abstract predicate checks(ControlFlowNode node, boolean branch);
}
}
}

View File

@@ -28,9 +28,7 @@ class Expr extends Expr_, AstNode {
/** Whether this expression may have a side effect (as determined purely from its syntax) */
predicate hasSideEffects() {
/* If an exception raised by this expression handled, count that as a side effect */
exists(ControlFlowNode n | n.getNode() = this |
n.getASuccessor().getNode() instanceof ExceptStmt
)
this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt
or
this.getASubExpression().hasSideEffects()
}
@@ -70,6 +68,8 @@ class Attribute extends Attribute_ {
/* syntax: Expr.name */
override Expr getASubExpression() { result = this.getObject() }
override AttrNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets the name of this attribute. That is the `name` in `obj.name` */
string getName() { result = Attribute_.super.getAttr() }
@@ -96,6 +96,8 @@ class Subscript extends Subscript_ {
}
Expr getObject() { result = Subscript_.super.getValue() }
override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
}
/** A call expression, such as `func(...)` */
@@ -111,6 +113,8 @@ class Call extends Call_ {
override string toString() { result = this.getFunc().toString() + "()" }
override CallNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets a tuple (*) argument of this call. */
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
@@ -196,6 +200,8 @@ class IfExp extends IfExp_ {
override Expr getASubExpression() {
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
}
override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
}
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
@@ -404,6 +410,8 @@ class PlaceHolder extends PlaceHolder_ {
override Expr getASubExpression() { none() }
override string toString() { result = "$" + this.getId() }
override NameNode getAFlowNode() { result = super.getAFlowNode() }
}
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
@@ -470,6 +478,8 @@ class Name extends Name_ {
override string toString() { result = this.getId() }
override NameNode getAFlowNode() { result = super.getAFlowNode() }
override predicate isArtificial() {
/* Artificial variable names in comprehensions all start with "." */
this.getId().charAt(0) = "."
@@ -575,6 +585,8 @@ abstract class NameConstant extends Name, ImmutableLiteral {
override predicate isConstant() { any() }
override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
override predicate isArtificial() { none() }
}

View File

@@ -1,7 +1,7 @@
overlay[local]
module;
import python as Py
import python
private import semmle.python.internal.CachedStages
private import codeql.controlflow.BasicBlock as BB
@@ -17,7 +17,7 @@ private import codeql.controlflow.BasicBlock as BB
*/
private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
exists(Py::Expr load_store | exists(Py::AugAssign aa | aa.getTarget() = load_store) |
exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) |
toAst(load) = load_store and
toAst(store) = load_store and
load.strictlyDominates(store)
@@ -25,7 +25,7 @@ private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
}
/** A non-dispatched getNode() to avoid negative recursion issues */
private Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
/**
* A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes,
@@ -35,19 +35,19 @@ private Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _)
class ControlFlowNode extends @py_flow_node {
/** Whether this control flow node is a load (including those in augmented assignments) */
predicate isLoad() {
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
}
/** Whether this control flow node is a store (including those in augmented assignments) */
predicate isStore() {
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
}
/** Whether this control flow node is a delete */
predicate isDelete() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
/** Whether this control flow node is a parameter */
predicate isParameter() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
/** Whether this control flow node is a store in an augmented assignment */
predicate isAugStore() { augstore(_, this) }
@@ -57,61 +57,61 @@ class ControlFlowNode extends @py_flow_node {
/** Whether this flow node corresponds to a literal */
predicate isLiteral() {
toAst(this) instanceof Py::Bytes
toAst(this) instanceof Bytes
or
toAst(this) instanceof Py::Dict
toAst(this) instanceof Dict
or
toAst(this) instanceof Py::DictComp
toAst(this) instanceof DictComp
or
toAst(this) instanceof Py::Set
toAst(this) instanceof Set
or
toAst(this) instanceof Py::SetComp
toAst(this) instanceof SetComp
or
toAst(this) instanceof Py::Ellipsis
toAst(this) instanceof Ellipsis
or
toAst(this) instanceof Py::GeneratorExp
toAst(this) instanceof GeneratorExp
or
toAst(this) instanceof Py::Lambda
toAst(this) instanceof Lambda
or
toAst(this) instanceof Py::ListComp
toAst(this) instanceof ListComp
or
toAst(this) instanceof Py::List
toAst(this) instanceof List
or
toAst(this) instanceof Py::Num
toAst(this) instanceof Num
or
toAst(this) instanceof Py::Tuple
toAst(this) instanceof Tuple
or
toAst(this) instanceof Py::Unicode
toAst(this) instanceof Unicode
or
toAst(this) instanceof Py::NameConstant
toAst(this) instanceof NameConstant
}
/** Whether this flow node corresponds to an attribute expression */
predicate isAttribute() { toAst(this) instanceof Py::Attribute }
predicate isAttribute() { toAst(this) instanceof Attribute }
/** Whether this flow node corresponds to an subscript expression */
predicate isSubscript() { toAst(this) instanceof Py::Subscript }
predicate isSubscript() { toAst(this) instanceof Subscript }
/** Whether this flow node corresponds to an import member */
predicate isImportMember() { toAst(this) instanceof Py::ImportMember }
predicate isImportMember() { toAst(this) instanceof ImportMember }
/** Whether this flow node corresponds to a call */
predicate isCall() { toAst(this) instanceof Py::Call }
predicate isCall() { toAst(this) instanceof Call }
/** Whether this flow node is the first in a module */
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Py::Module }
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module }
/** Whether this flow node corresponds to an import */
predicate isImport() { toAst(this) instanceof Py::ImportExpr }
predicate isImport() { toAst(this) instanceof ImportExpr }
/** Whether this flow node corresponds to a conditional expression */
predicate isIfExp() { toAst(this) instanceof Py::IfExp }
predicate isIfExp() { toAst(this) instanceof IfExp }
/** Whether this flow node corresponds to a function definition expression */
predicate isFunction() { toAst(this) instanceof Py::FunctionExpr }
predicate isFunction() { toAst(this) instanceof FunctionExpr }
/** Whether this flow node corresponds to a class definition expression */
predicate isClass() { toAst(this) instanceof Py::ClassExpr }
predicate isClass() { toAst(this) instanceof ClassExpr }
/** Gets a predecessor of this flow node */
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
@@ -123,25 +123,25 @@ class ControlFlowNode extends @py_flow_node {
ControlFlowNode getImmediateDominator() { py_idoms(this, result) }
/** Gets the syntactic element corresponding to this flow node */
Py::AstNode getNode() { py_flow_bb_node(this, result, _, _) }
AstNode getNode() { py_flow_bb_node(this, result, _, _) }
/** Gets a textual representation of this element. */
cached
string toString() {
Stages::AST::ref() and
// Since modules can have ambigous names, entry nodes can too, if we do not collate them.
exists(Py::Scope s | s.getEntryNode() = this |
exists(Scope s | s.getEntryNode() = this |
result = "Entry node for " + concat( | | s.toString(), ",")
)
or
exists(Py::Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
or
not exists(Py::Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
result = "ControlFlowNode for " + this.getNode().toString()
}
/** Gets the location of this ControlFlowNode */
Py::Location getLocation() { result = this.getNode().getLocation() }
Location getLocation() { result = this.getNode().getLocation() }
/** Whether this flow node is the first in its scope */
predicate isEntryNode() { py_scope_flow(this, _, -1) }
@@ -151,9 +151,9 @@ class ControlFlowNode extends @py_flow_node {
/** Gets the scope containing this flow node */
cached
Py::Scope getScope() {
Scope getScope() {
Stages::AST::ref() and
if this.getNode() instanceof Py::Scope
if this.getNode() instanceof Scope
then
/* Entry or exit node */
result = this.getNode()
@@ -161,7 +161,7 @@ class ControlFlowNode extends @py_flow_node {
}
/** Gets the enclosing module */
Py::Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
/** Gets a successor for this node if the relevant condition is True. */
ControlFlowNode getATrueSuccessor() {
@@ -188,7 +188,7 @@ class ControlFlowNode extends @py_flow_node {
}
/** Whether the scope may be exited as a result of this node raising an exception */
predicate isExceptionalExit(Py::Scope s) { py_scope_flow(this, s, 1) }
predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) }
/** Whether this node is a normal (non-exceptional) exit */
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
@@ -236,7 +236,7 @@ class ControlFlowNode extends @py_flow_node {
/* join-ordering helper for `getAChild() */
pragma[noinline]
private ControlFlowNode getExprChild(BasicBlock dom) {
this.getNode().(Py::Expr).getAChildNode() = result.getNode() and
this.getNode().(Expr).getAChildNode() = result.getNode() and
result.getBasicBlock().dominates(dom) and
not this instanceof UnaryExprNode
}
@@ -249,16 +249,16 @@ class ControlFlowNode extends @py_flow_node {
*/
private class AnyNode extends ControlFlowNode {
override Py::AstNode getNode() { result = super.getNode() }
override AstNode getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a call expression, such as `func(...)` */
class CallNode extends ControlFlowNode {
CallNode() { toAst(this) instanceof Py::Call }
CallNode() { toAst(this) instanceof Call }
/** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */
ControlFlowNode getFunction() {
exists(Py::Call c |
exists(Call c |
this.getNode() = c and
c.getFunc() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -267,7 +267,7 @@ class CallNode extends ControlFlowNode {
/** Gets the flow node corresponding to the n'th positional argument of the call corresponding to this flow node */
ControlFlowNode getArg(int n) {
exists(Py::Call c |
exists(Call c |
this.getNode() = c and
c.getArg(n) = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -276,7 +276,7 @@ class CallNode extends ControlFlowNode {
/** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */
ControlFlowNode getArgByName(string name) {
exists(Py::Call c, Py::Keyword k |
exists(Call c, Keyword k |
this.getNode() = c and
k = c.getANamedArg() and
k.getValue() = result.getNode() and
@@ -292,7 +292,7 @@ class CallNode extends ControlFlowNode {
result = this.getArgByName(_)
}
override Py::Call getNode() { result = super.getNode() }
override Call getNode() { result = super.getNode() }
predicate isDecoratorCall() {
this.isClassDecoratorCall()
@@ -301,11 +301,11 @@ class CallNode extends ControlFlowNode {
}
predicate isClassDecoratorCall() {
exists(Py::ClassExpr cls | this.getNode() = cls.getADecoratorCall())
exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall())
}
predicate isFunctionDecoratorCall() {
exists(Py::FunctionExpr func | this.getNode() = func.getADecoratorCall())
exists(FunctionExpr func | this.getNode() = func.getADecoratorCall())
}
/** Gets the first tuple (*) argument of this call, if any. */
@@ -323,11 +323,11 @@ class CallNode extends ControlFlowNode {
/** A control flow corresponding to an attribute expression, such as `value.attr` */
class AttrNode extends ControlFlowNode {
AttrNode() { toAst(this) instanceof Py::Attribute }
AttrNode() { toAst(this) instanceof Attribute }
/** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */
ControlFlowNode getObject() {
exists(Py::Attribute a |
exists(Attribute a |
this.getNode() = a and
a.getObject() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -339,7 +339,7 @@ class AttrNode extends ControlFlowNode {
* with the matching name
*/
ControlFlowNode getObject(string name) {
exists(Py::Attribute a |
exists(Attribute a |
this.getNode() = a and
a.getObject() = result.getNode() and
a.getName() = name and
@@ -348,57 +348,57 @@ class AttrNode extends ControlFlowNode {
}
/** Gets the attribute name of the attribute expression corresponding to this flow node */
string getName() { exists(Py::Attribute a | this.getNode() = a and a.getName() = result) }
string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) }
override Py::Attribute getNode() { result = super.getNode() }
override Attribute getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a `from ... import ...` expression */
class ImportMemberNode extends ControlFlowNode {
ImportMemberNode() { toAst(this) instanceof Py::ImportMember }
ImportMemberNode() { toAst(this) instanceof ImportMember }
/**
* Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node,
* with the matching name
*/
ControlFlowNode getModule(string name) {
exists(Py::ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
i.getName() = name and
result.getBasicBlock().dominates(this.getBasicBlock())
)
}
override Py::ImportMember getNode() { result = super.getNode() }
override ImportMember getNode() { result = super.getNode() }
}
/** A control flow node corresponding to an artificial expression representing an import */
class ImportExprNode extends ControlFlowNode {
ImportExprNode() { toAst(this) instanceof Py::ImportExpr }
ImportExprNode() { toAst(this) instanceof ImportExpr }
override Py::ImportExpr getNode() { result = super.getNode() }
override ImportExpr getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a `from ... import *` statement */
class ImportStarNode extends ControlFlowNode {
ImportStarNode() { toAst(this) instanceof Py::ImportStar }
ImportStarNode() { toAst(this) instanceof ImportStar }
/** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */
ControlFlowNode getModule() {
exists(Py::ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
result.getBasicBlock().dominates(this.getBasicBlock())
)
}
override Py::ImportStar getNode() { result = super.getNode() }
override ImportStar getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a subscript expression, such as `value[slice]` */
class SubscriptNode extends ControlFlowNode {
SubscriptNode() { toAst(this) instanceof Py::Subscript }
SubscriptNode() { toAst(this) instanceof Subscript }
/** flow node corresponding to the value of the sequence in a subscript operation */
ControlFlowNode getObject() {
exists(Py::Subscript s |
exists(Subscript s |
this.getNode() = s and
s.getObject() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -407,23 +407,23 @@ class SubscriptNode extends ControlFlowNode {
/** flow node corresponding to the index in a subscript operation */
ControlFlowNode getIndex() {
exists(Py::Subscript s |
exists(Subscript s |
this.getNode() = s and
s.getIndex() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock())
)
}
override Py::Subscript getNode() { result = super.getNode() }
override Subscript getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a comparison operation, such as `x<y` */
class CompareNode extends ControlFlowNode {
CompareNode() { toAst(this) instanceof Py::Compare }
CompareNode() { toAst(this) instanceof Compare }
/** Whether left and right are a pair of operands for this comparison */
predicate operands(ControlFlowNode left, Py::Cmpop op, ControlFlowNode right) {
exists(Py::Compare c, Py::Expr eleft, Py::Expr eright |
predicate operands(ControlFlowNode left, Cmpop op, ControlFlowNode right) {
exists(Compare c, Expr eleft, Expr eright |
this.getNode() = c and left.getNode() = eleft and right.getNode() = eright
|
eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0)
@@ -436,26 +436,26 @@ class CompareNode extends ControlFlowNode {
right.getBasicBlock().dominates(this.getBasicBlock())
}
override Py::Compare getNode() { result = super.getNode() }
override Compare getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a conditional expression such as, `body if test else orelse` */
class IfExprNode extends ControlFlowNode {
IfExprNode() { toAst(this) instanceof Py::IfExp }
IfExprNode() { toAst(this) instanceof IfExp }
/** flow node corresponding to one of the operands of an if-expression */
ControlFlowNode getAnOperand() { result = this.getAPredecessor() }
override Py::IfExp getNode() { result = super.getNode() }
override IfExp getNode() { result = super.getNode() }
}
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
class AssignmentExprNode extends ControlFlowNode {
AssignmentExprNode() { toAst(this) instanceof Py::AssignExpr }
AssignmentExprNode() { toAst(this) instanceof AssignExpr }
/** Gets the flow node corresponding to the left-hand side of the assignment expression */
ControlFlowNode getTarget() {
exists(Py::AssignExpr a |
exists(AssignExpr a |
this.getNode() = a and
a.getTarget() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -464,27 +464,27 @@ class AssignmentExprNode extends ControlFlowNode {
/** Gets the flow node corresponding to the right-hand side of the assignment expression */
ControlFlowNode getValue() {
exists(Py::AssignExpr a |
exists(AssignExpr a |
this.getNode() = a and
a.getValue() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock())
)
}
override Py::AssignExpr getNode() { result = super.getNode() }
override AssignExpr getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a binary expression, such as `x + y` */
class BinaryExprNode extends ControlFlowNode {
BinaryExprNode() { toAst(this) instanceof Py::BinaryExpr }
BinaryExprNode() { toAst(this) instanceof BinaryExpr }
/** flow node corresponding to one of the operands of a binary expression */
ControlFlowNode getAnOperand() { result = this.getLeft() or result = this.getRight() }
override Py::BinaryExpr getNode() { result = super.getNode() }
override BinaryExpr getNode() { result = super.getNode() }
ControlFlowNode getLeft() {
exists(Py::BinaryExpr b |
exists(BinaryExpr b |
this.getNode() = b and
result.getNode() = b.getLeft() and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -492,7 +492,7 @@ class BinaryExprNode extends ControlFlowNode {
}
ControlFlowNode getRight() {
exists(Py::BinaryExpr b |
exists(BinaryExpr b |
this.getNode() = b and
result.getNode() = b.getRight() and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -500,11 +500,11 @@ class BinaryExprNode extends ControlFlowNode {
}
/** Gets the operator of this binary expression node. */
Py::Operator getOp() { result = this.getNode().getOp() }
Operator getOp() { result = this.getNode().getOp() }
/** Whether left and right are a pair of operands for this binary expression */
predicate operands(ControlFlowNode left, Py::Operator op, ControlFlowNode right) {
exists(Py::BinaryExpr b, Py::Expr eleft, Py::Expr eright |
predicate operands(ControlFlowNode left, Operator op, ControlFlowNode right) {
exists(BinaryExpr b, Expr eleft, Expr eright |
this.getNode() = b and left.getNode() = eleft and right.getNode() = eright
|
eleft = b.getLeft() and eright = b.getRight() and op = b.getOp()
@@ -516,20 +516,20 @@ class BinaryExprNode extends ControlFlowNode {
/** A control flow node corresponding to a boolean shortcut (and/or) operation */
class BoolExprNode extends ControlFlowNode {
BoolExprNode() { toAst(this) instanceof Py::BoolExpr }
BoolExprNode() { toAst(this) instanceof BoolExpr }
/** flow node corresponding to one of the operands of a boolean expression */
ControlFlowNode getAnOperand() {
exists(Py::BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
exists(BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
this.getBasicBlock().dominates(result.getBasicBlock())
}
override Py::BoolExpr getNode() { result = super.getNode() }
override BoolExpr getNode() { result = super.getNode() }
}
/** A control flow node corresponding to a unary expression: (`+x`), (`-x`) or (`~x`) */
class UnaryExprNode extends ControlFlowNode {
UnaryExprNode() { toAst(this) instanceof Py::UnaryExpr }
UnaryExprNode() { toAst(this) instanceof UnaryExpr }
/**
* Gets flow node corresponding to the operand of a unary expression.
@@ -540,7 +540,7 @@ class UnaryExprNode extends ControlFlowNode {
*/
ControlFlowNode getOperand() { result = this.getAPredecessor() }
override Py::UnaryExpr getNode() { result = super.getNode() }
override UnaryExpr getNode() { result = super.getNode() }
override ControlFlowNode getAChild() { result = this.getAPredecessor() }
}
@@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
cached
DefinitionNode() {
Stages::AST::ref() and
exists(Py::Assign a | this.getNode() = a.getATarget())
exists(Assign a | a.getATarget().getAFlowNode() = this)
or
exists(Py::AssignExpr a | this.getNode() = a.getTarget())
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
or
exists(Py::AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
or
exists(Py::Alias a | this.getNode() = a.getAsname())
exists(Alias a | a.getAsname().getAFlowNode() = this)
or
augstore(_, this)
or
// `x, y = 1, 2` where LHS is a combination of list or tuples
exists(Py::Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget()))
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
or
exists(Py::For for | this.getNode() = for.getTarget())
exists(For for | for.getTarget().getAFlowNode() = this)
or
exists(Py::Parameter param | this.getNode() = param.asName() and exists(param.getDefault()))
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault()))
}
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
ControlFlowNode getValue() {
result.getNode() = assigned_value(this.getNode()) and
result = assigned_value(this.getNode()).getAFlowNode() and
(
result.getBasicBlock().dominates(this.getBasicBlock())
or
@@ -584,16 +584,16 @@ class DefinitionNode extends ControlFlowNode {
// since the default value for a parameter is evaluated in the same basic block as
// the function definition, but the parameter belongs to the basic block of the function,
// there is no dominance relationship between the two.
exists(Py::Parameter param | this.getNode() = param.asName())
exists(Parameter param | this = param.asName().getAFlowNode())
)
}
}
private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) {
exists(Py::Expr elt |
elt = list_or_tuple.(Py::Tuple).getAnElt()
private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
exists(Expr elt |
elt = list_or_tuple.(Tuple).getAnElt()
or
elt = list_or_tuple.(Py::List).getAnElt()
elt = list_or_tuple.(List).getAnElt()
|
result = elt
or
@@ -603,12 +603,12 @@ private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) {
/**
* A control flow node corresponding to a deletion statement, such as `del x`.
* There can be multiple `DeletionNode`s for each `Py::Delete` such that each
* There can be multiple `DeletionNode`s for each `Delete` such that each
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
* `NameNode('a') -> DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`.
*/
class DeletionNode extends ControlFlowNode {
DeletionNode() { toAst(this) instanceof Py::Delete }
DeletionNode() { toAst(this) instanceof Delete }
/** Gets the unique target of this deletion node. */
ControlFlowNode getTarget() { result.getASuccessor() = this }
@@ -617,9 +617,9 @@ class DeletionNode extends ControlFlowNode {
/** A control flow node corresponding to a sequence (tuple or list) literal */
abstract class SequenceNode extends ControlFlowNode {
SequenceNode() {
toAst(this) instanceof Py::Tuple
toAst(this) instanceof Tuple
or
toAst(this) instanceof Py::List
toAst(this) instanceof List
}
/** Gets the control flow node for an element of this sequence */
@@ -632,11 +632,11 @@ abstract class SequenceNode extends ControlFlowNode {
/** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */
class TupleNode extends SequenceNode {
TupleNode() { toAst(this) instanceof Py::Tuple }
TupleNode() { toAst(this) instanceof Tuple }
override ControlFlowNode getElement(int n) {
Stages::AST::ref() and
exists(Py::Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
(
result.getBasicBlock().dominates(this.getBasicBlock())
or
@@ -647,10 +647,10 @@ class TupleNode extends SequenceNode {
/** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */
class ListNode extends SequenceNode {
ListNode() { toAst(this) instanceof Py::List }
ListNode() { toAst(this) instanceof List }
override ControlFlowNode getElement(int n) {
exists(Py::List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
(
result.getBasicBlock().dominates(this.getBasicBlock())
or
@@ -661,10 +661,10 @@ class ListNode extends SequenceNode {
/** A control flow node corresponding to a set expression, such as `{ 1, 3, 5, 7, 9 }` */
class SetNode extends ControlFlowNode {
SetNode() { toAst(this) instanceof Py::Set }
SetNode() { toAst(this) instanceof Set }
ControlFlowNode getAnElement() {
exists(Py::Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
(
result.getBasicBlock().dominates(this.getBasicBlock())
or
@@ -675,20 +675,20 @@ class SetNode extends ControlFlowNode {
/** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */
class DictNode extends ControlFlowNode {
DictNode() { toAst(this) instanceof Py::Dict }
DictNode() { toAst(this) instanceof Dict }
/**
* Gets a key of this dictionary literal node, for those items that have keys
* E.g, in {'a':1, **b} this returns only 'a'
*/
ControlFlowNode getAKey() {
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
result.getBasicBlock().dominates(this.getBasicBlock())
}
/** Gets a value of this dictionary literal node */
ControlFlowNode getAValue() {
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
result.getBasicBlock().dominates(this.getBasicBlock())
}
}
@@ -712,23 +712,21 @@ class IterableNode extends ControlFlowNode {
}
}
private Py::AstNode assigned_value(Py::Expr lhs) {
private AstNode assigned_value(Expr lhs) {
/* lhs = result */
exists(Py::Assign a | a.getATarget() = lhs and result = a.getValue())
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
or
/* lhs := result */
exists(Py::AssignExpr a | a.getTarget() = lhs and result = a.getValue())
exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue())
or
/* lhs : annotation = result */
exists(Py::AnnAssign a | a.getTarget() = lhs and result = a.getValue())
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
or
/* import result as lhs */
exists(Py::Alias a | a.getAsname() = lhs and result = a.getValue())
exists(Alias a | a.getAsname() = lhs and result = a.getValue())
or
/* lhs += x => result = (lhs + x) */
exists(Py::AugAssign a, Py::BinaryExpr b |
b = a.getOperation() and result = b and lhs = b.getLeft()
)
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
or
/*
* ..., lhs, ... = ..., result, ...
@@ -736,31 +734,31 @@ private Py::AstNode assigned_value(Py::Expr lhs) {
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
*/
exists(Py::Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
or
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
result.(Py::For).getTarget() = lhs
result.(For).getTarget() = lhs
or
exists(Py::Parameter param | lhs = param.asName() and result = param.getDefault())
exists(Parameter param | lhs = param.asName() and result = param.getDefault())
}
predicate nested_sequence_assign(
Py::Expr left_parent, Py::Expr right_parent, Py::Expr left_result, Py::Expr right_result
Expr left_parent, Expr right_parent, Expr left_result, Expr right_result
) {
exists(Py::Assign a |
exists(Assign a |
a.getATarget().getASubExpression*() = left_parent and
a.getValue().getASubExpression*() = right_parent
) and
exists(int i, Py::Expr left_elem, Py::Expr right_elem |
exists(int i, Expr left_elem, Expr right_elem |
(
left_elem = left_parent.(Py::Tuple).getElt(i)
left_elem = left_parent.(Tuple).getElt(i)
or
left_elem = left_parent.(Py::List).getElt(i)
left_elem = left_parent.(List).getElt(i)
) and
(
right_elem = right_parent.(Py::Tuple).getElt(i)
right_elem = right_parent.(Tuple).getElt(i)
or
right_elem = right_parent.(Py::List).getElt(i)
right_elem = right_parent.(List).getElt(i)
)
|
left_result = left_elem and right_result = right_elem
@@ -771,9 +769,9 @@ predicate nested_sequence_assign(
/** A flow node for a `for` statement. */
class ForNode extends ControlFlowNode {
ForNode() { toAst(this) instanceof Py::For }
ForNode() { toAst(this) instanceof For }
override Py::For getNode() { result = super.getNode() }
override For getNode() { result = super.getNode() }
/** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */
predicate iterates(ControlFlowNode target, ControlFlowNode sequence) {
@@ -784,7 +782,7 @@ class ForNode extends ControlFlowNode {
/** Gets the sequence node for this `for` statement. */
ControlFlowNode getSequence() {
exists(Py::For for |
exists(For for |
toAst(this) = for and
for.getIter() = result.getNode()
|
@@ -794,7 +792,7 @@ class ForNode extends ControlFlowNode {
/** A possible `target` for this `for` statement, not accounting for loop unrolling */
private ControlFlowNode possibleTarget() {
exists(Py::For for |
exists(For for |
toAst(this) = for and
for.getTarget() = result.getNode() and
this.getBasicBlock().dominates(result.getBasicBlock())
@@ -811,11 +809,11 @@ class ForNode extends ControlFlowNode {
/** A flow node for a `raise` statement */
class RaiseStmtNode extends ControlFlowNode {
RaiseStmtNode() { toAst(this) instanceof Py::Raise }
RaiseStmtNode() { toAst(this) instanceof Raise }
/** Gets the control flow node for the exception raised by this raise statement */
ControlFlowNode getException() {
exists(Py::Raise r |
exists(Raise r |
r = toAst(this) and
r.getException() = toAst(result) and
result.getBasicBlock().dominates(this.getBasicBlock())
@@ -829,36 +827,36 @@ class RaiseStmtNode extends ControlFlowNode {
*/
class NameNode extends ControlFlowNode {
NameNode() {
exists(Py::Name n | py_flow_bb_node(this, n, _, _))
exists(Name n | py_flow_bb_node(this, n, _, _))
or
exists(Py::PlaceHolder p | py_flow_bb_node(this, p, _, _))
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
}
/** Whether this flow node defines the variable `v`. */
predicate defines(Py::Variable v) {
exists(Py::Name d | this.getNode() = d and d.defines(v)) and
predicate defines(Variable v) {
exists(Name d | this.getNode() = d and d.defines(v)) and
not this.isLoad()
}
/** Whether this flow node deletes the variable `v`. */
predicate deletes(Py::Variable v) { exists(Py::Name d | this.getNode() = d and d.deletes(v)) }
predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) }
/** Whether this flow node uses the variable `v`. */
predicate uses(Py::Variable v) {
predicate uses(Variable v) {
this.isLoad() and
exists(Py::Name u | this.getNode() = u and u.uses(v))
exists(Name u | this.getNode() = u and u.uses(v))
or
exists(Py::PlaceHolder u |
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Py::Load
exists(PlaceHolder u |
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load
)
or
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
}
string getId() {
result = this.getNode().(Py::Name).getId()
result = this.getNode().(Name).getId()
or
result = this.getNode().(Py::PlaceHolder).getId()
result = this.getNode().(PlaceHolder).getId()
}
/** Whether this is a use of a local variable. */
@@ -870,84 +868,82 @@ class NameNode extends ControlFlowNode {
/** Whether this is a use of a global (including builtin) variable. */
predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) }
predicate isSelf() {
exists(Py::SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this)
}
predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) }
}
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
class NameConstantNode extends NameNode {
NameConstantNode() { exists(Py::NameConstant n | py_flow_bb_node(this, n, _, _)) }
NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) }
/*
* We ought to override uses as well, but that has
* a serious performance impact.
* deprecated predicate uses(Py::Variable v) { none() }
* deprecated predicate uses(Variable v) { none() }
*/
}
/** A control flow node corresponding to a starred expression, `*a`. */
class StarredNode extends ControlFlowNode {
StarredNode() { toAst(this) instanceof Py::Starred }
StarredNode() { toAst(this) instanceof Starred }
ControlFlowNode getValue() { toAst(result) = toAst(this).(Py::Starred).getValue() }
ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() }
}
/** The ControlFlowNode for an 'except' statement. */
class ExceptFlowNode extends ControlFlowNode {
ExceptFlowNode() { this.getNode() instanceof Py::ExceptStmt }
ExceptFlowNode() { this.getNode() instanceof ExceptStmt }
/**
* Gets the type handled by this exception handler.
* `Py::ExceptionType` in `except Py::ExceptionType as e:`
* `ExceptionType` in `except ExceptionType as e:`
*/
ControlFlowNode getType() {
exists(Py::ExceptStmt ex |
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result.getNode() = ex.getType()
result = ex.getType().getAFlowNode()
)
}
/**
* Gets the name assigned to the handled exception, if any.
* `e` in `except Py::ExceptionType as e:`
* `e` in `except ExceptionType as e:`
*/
ControlFlowNode getName() {
exists(Py::ExceptStmt ex |
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result.getNode() = ex.getName()
result = ex.getName().getAFlowNode()
)
}
}
/** The ControlFlowNode for an 'except*' statement. */
class ExceptGroupFlowNode extends ControlFlowNode {
ExceptGroupFlowNode() { this.getNode() instanceof Py::ExceptGroupStmt }
ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt }
/**
* Gets the type handled by this exception handler.
* `Py::ExceptionType` in `except* Py::ExceptionType as e:`
* `ExceptionType` in `except* ExceptionType as e:`
*/
ControlFlowNode getType() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getType()
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
}
/**
* Gets the name assigned to the handled exception, if any.
* `e` in `except* Py::ExceptionType as e:`
* `e` in `except* ExceptionType as e:`
*/
ControlFlowNode getName() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getName()
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
}
}
private module Scopes {
private predicate fast_local(NameNode n) {
exists(Py::FastLocalVariable v |
exists(FastLocalVariable v |
n.uses(v) and
v.getScope() = n.getScope()
)
@@ -956,15 +952,15 @@ private module Scopes {
predicate local(NameNode n) {
fast_local(n)
or
exists(Py::SsaVariable var |
exists(SsaVariable var |
var.getAUse() = n and
n.getScope() instanceof Py::Class and
n.getScope() instanceof Class and
exists(var.getDefinition())
)
}
predicate non_local(NameNode n) {
exists(Py::FastLocalVariable flv |
exists(FastLocalVariable flv |
flv.getALoad() = n.getNode() and
not flv.getScope() = n.getScope()
)
@@ -972,20 +968,20 @@ private module Scopes {
// magic is fine, but we get questionable join-ordering of it
pragma[nomagic]
predicate use_of_global_variable(NameNode n, Py::Module scope, string name) {
predicate use_of_global_variable(NameNode n, Module scope, string name) {
n.isLoad() and
not non_local(n) and
not exists(Py::SsaVariable var | var.getAUse() = n |
var.getVariable() instanceof Py::FastLocalVariable
not exists(SsaVariable var | var.getAUse() = n |
var.getVariable() instanceof FastLocalVariable
or
n.getScope() instanceof Py::Class and
n.getScope() instanceof Class and
not maybe_undefined(var)
) and
name = n.getId() and
scope = n.getEnclosingModule()
}
private predicate maybe_undefined(Py::SsaVariable var) {
private predicate maybe_undefined(SsaVariable var) {
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
or
var.getDefinition().isDelete()
@@ -1062,13 +1058,13 @@ class BasicBlock extends @py_flow_node {
private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() }
private predicate startLocationInfo(string file, int line, int col) {
if this.firstNode().getNode() instanceof Py::Scope
if this.firstNode().getNode() instanceof Scope
then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _)
else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _)
}
private predicate endLocationInfo(int endl, int endc) {
if this.getLastNode().getNode() instanceof Py::Scope and not this.oneNodeBlock()
if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock()
then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc)
else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc)
}
@@ -1085,7 +1081,7 @@ class BasicBlock extends @py_flow_node {
/** Whether flow from this basic block reaches a normal exit from its scope */
predicate reachesExit() {
exists(Py::Scope s | s.getANormalExit().getBasicBlock() = this)
exists(Scope s | s.getANormalExit().getBasicBlock() = this)
or
this.getASuccessor().reachesExit()
}
@@ -1126,7 +1122,7 @@ class BasicBlock extends @py_flow_node {
/** Gets the scope of this block */
pragma[nomagic]
Py::Scope getScope() {
Scope getScope() {
exists(ControlFlowNode n | n.getBasicBlock() = this |
/* Take care not to use an entry or exit node as that node's scope will be the outer scope */
not py_scope_flow(n, _, -1) and
@@ -1149,17 +1145,17 @@ class BasicBlock extends @py_flow_node {
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
/**
* Gets the `Py::ConditionBlock`, if any, that controls this block and
* does not control any other `Py::ConditionBlock`s that control this block.
* That is the `Py::ConditionBlock` that is closest dominator.
* Gets the `ConditionBlock`, if any, that controls this block and
* does not control any other `ConditionBlock`s that control this block.
* That is the `ConditionBlock` that is closest dominator.
*/
Py::ConditionBlock getImmediatelyControllingBlock() {
ConditionBlock getImmediatelyControllingBlock() {
result = this.nonControllingImmediateDominator*().getImmediateDominator()
}
private BasicBlock nonControllingImmediateDominator() {
result = this.getImmediateDominator() and
not result.(Py::ConditionBlock).controls(this, _)
not result.(ConditionBlock).controls(this, _)
}
/**
@@ -1179,7 +1175,7 @@ private class ControlFlowNodeAlias = ControlFlowNode;
final private class FinalBasicBlock = BasicBlock;
module Cfg implements BB::CfgSig<Py::Location> {
module Cfg implements BB::CfgSig<Location> {
private import codeql.controlflow.SuccessorType
class ControlFlowNode = ControlFlowNodeAlias;
@@ -1190,7 +1186,7 @@ module Cfg implements BB::CfgSig<Py::Location> {
// Using the location of the first node is simple
// and we just need a way to identify the basic block
// during debugging, so this will be serviceable.
Py::Location getLocation() { result = super.getNode(0).getLocation() }
Location getLocation() { result = super.getNode(0).getLocation() }
int length() { result = count(int i | exists(this.getNode(i))) }

View File

@@ -2,7 +2,6 @@ overlay[local]
module;
import python
private import semmle.python.controlflow.internal.Cfg as Cfg
/**
* A function, independent of defaults and binding.
@@ -154,16 +153,8 @@ class Function extends Function_, Scope, AstNode {
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
/**
* DEPRECATED: bind a `Return` node explicitly instead, e.g.
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
* This API is being phased out together with `AstNode.getAFlowNode()` to
* untangle the AST and CFG hierarchies.
*
* Gets a control flow node for a return value of this function, from the
* new (shared) CFG.
*/
deprecated Cfg::ControlFlowNode getAReturnValueFlowNode() {
/** Gets a control flow node for a return value of this function */
ControlFlowNode getAReturnValueFlowNode() {
exists(Return ret |
ret.getScope() = this and
ret.getValue() = result.getNode()

View File

@@ -162,6 +162,8 @@ class ImportMember extends ImportMember_ {
string getImportedModuleName() {
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
}
override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
}
/** An import statement */

View File

@@ -46,23 +46,20 @@ class SelfAttributeRead extends SelfAttribute {
}
predicate guardedByHasattr() {
exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
this_.getNode() = this and obj_.getNode() = this.getObject()
|
var.getAUse() = obj_ and
exists(Variable var, ControlFlowNode n |
var.getAUse() = this.getObject().getAFlowNode() and
hasattr(n, var.getAUse(), this.getName()) and
n.strictlyDominates(this_)
n.strictlyDominates(this.getAFlowNode())
)
}
pragma[noinline]
predicate locallyDefined() {
exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
store_.getNode() = store and this_.getNode() = this
|
exists(SelfAttributeStore store |
this.getName() = store.getName() and
this.getScope() = store.getScope() and
store_.strictlyDominates(this_)
this.getScope() = store.getScope()
|
store.getAFlowNode().strictlyDominates(this.getAFlowNode())
)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,36 @@
/** Provides commonly used BarrierGuards. */
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private predicate constCompare(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
exists(Cfg::CompareNode cn | cn = g |
exists(ImmutableLiteral const, Cmpop op, Cfg::ControlFlowNode c |
c.getNode() = const and
(
op = any(Eq eq) and branch = true
or
op = any(NotEq ne) and branch = false
)
|
cn.operands(c, op, node)
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
exists(CompareNode cn | cn = g |
exists(ImmutableLiteral const, Cmpop op |
op = any(Eq eq) and branch = true
or
cn.operands(node, op, c)
op = any(NotEq ne) and branch = false
|
cn.operands(const.getAFlowNode(), op, node)
or
cn.operands(node, op, const.getAFlowNode())
)
or
exists(NameConstant const, Cmpop op, Cfg::ControlFlowNode c |
c.getNode() = const and
(
op = any(Is is_) and branch = true
or
op = any(IsNot isn) and branch = false
)
|
cn.operands(c, op, node)
exists(NameConstant const, Cmpop op |
op = any(Is is_) and branch = true
or
cn.operands(node, op, c)
op = any(IsNot isn) and branch = false
|
cn.operands(const.getAFlowNode(), op, node)
or
cn.operands(node, op, const.getAFlowNode())
)
or
exists(Cfg::IterableNode const_iterable, Cmpop op |
exists(IterableNode const_iterable, Cmpop op |
op = any(In in_) and branch = true
or
op = any(NotIn ni) and branch = false
|
forall(Cfg::ControlFlowNode elem | elem = const_iterable.getAnElement() |
forall(ControlFlowNode elem | elem = const_iterable.getAnElement() |
elem.getNode() instanceof ImmutableLiteral
) and
cn.operands(node, op, const_iterable)

View File

@@ -4,7 +4,6 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
private import semmle.python.Frameworks
@@ -106,7 +105,7 @@ private module SensitiveDataModeling {
or
// to cover functions that we don't have the definition for, and where the
// reference to the function has not already been marked as being sensitive
this.getFunction().asCfgNode().(Cfg::NameNode).getId() = sensitiveString(classification)
this.getFunction().asCfgNode().(NameNode).getId() = sensitiveString(classification)
}
override SensitiveDataClassification getClassification() { result = classification }
@@ -252,12 +251,12 @@ private module SensitiveDataModeling {
SensitiveDataClassification classification;
SensitiveVariableAssignment() {
exists(Cfg::DefinitionNode def |
def.(Cfg::NameNode).getId() = sensitiveString(classification) and
exists(DefinitionNode def |
def.(NameNode).getId() = sensitiveString(classification) and
(
this.asCfgNode() = def.getValue()
or
this.asCfgNode() = def.getValue().(Cfg::ForNode).getSequence()
this.asCfgNode() = def.getValue().(ForNode).getSequence()
) and
not this.asExpr() instanceof FunctionExpr and
not this.asExpr() instanceof ClassExpr
@@ -294,7 +293,7 @@ private module SensitiveDataModeling {
SensitiveDataClassification classification;
SensitiveSubscript() {
this.asCfgNode().(Cfg::SubscriptNode).getIndex() =
this.asCfgNode().(SubscriptNode).getIndex() =
sensitiveLookupStringConst(classification).asCfgNode()
}

Some files were not shown because too many files have changed in this diff Show More