mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #20622 from owen-mc/docs/fix-dataflow-examples
Docs: add path query example to data flow docs
This commit is contained in:
@@ -314,7 +314,7 @@ Exercise 2: Write a query that finds all hard-coded strings used to create a ``h
|
||||
|
||||
Exercise 3: Write a class that represents flow sources from ``getenv``. (`Answer <#exercise-3>`__)
|
||||
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``getenv`` to ``gethostbyname``. (`Answer <#exercise-4>`__)
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``getenv`` to ``gethostbyname``. (`Answer <#exercise-4>`__ `Answer as a path query <#path-query-example>`__)
|
||||
|
||||
Answers
|
||||
-------
|
||||
@@ -411,6 +411,48 @@ Exercise 4
|
||||
GetenvToGethostbynameFlow::flow(source, sink)
|
||||
select getenv, fc
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the answer to exercise 4 above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id getenv-to-gethostbyname
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
|
||||
class GetenvSource extends DataFlow::Node {
|
||||
GetenvSource() { this.asIndirectExpr(1).(FunctionCall).getTarget().hasGlobalName("getenv") }
|
||||
}
|
||||
|
||||
module GetenvToGethostbynameConfiguration implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof GetenvSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall fc |
|
||||
sink.asIndirectExpr(1) = fc.getArgument(0) and
|
||||
fc.getTarget().hasName("gethostbyname")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module GetenvToGethostbynameFlow = DataFlow::Global<GetenvToGethostbynameConfiguration>;
|
||||
|
||||
import GetenvToGethostbynameFlow::PathGraph
|
||||
|
||||
from GetenvToGethostbynameFlow::PathNode source, GetenvToGethostbynameFlow::PathNode sink
|
||||
where GetenvToGethostbynameFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This file access uses data from $@.",
|
||||
source, "user-controllable input."
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ Exercise 2: Find all hard-coded strings passed to ``System.Uri``, using global d
|
||||
|
||||
Exercise 3: Define a class that represents flow sources from ``System.Environment.GetEnvironmentVariable``. (`Answer <#exercise-3>`__)
|
||||
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``System.Environment.GetEnvironmentVariable`` to ``System.Uri``. (`Answer <#exercise-4>`__)
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``System.Environment.GetEnvironmentVariable`` to ``System.Uri``. (`Answer <#exercise-4>`__ `Answer as a path query <#path-query-example>`__)
|
||||
|
||||
Extending library data flow
|
||||
---------------------------
|
||||
@@ -537,6 +537,48 @@ This can be adapted from the ``SystemUriFlow`` class:
|
||||
}
|
||||
}
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the answer to exercise 4 above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id getenv-to-gethostbyname
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
class EnvironmentVariableFlowSource extends DataFlow::ExprNode {
|
||||
EnvironmentVariableFlowSource() {
|
||||
this.getExpr().(MethodCall).getTarget().hasQualifiedName("System.Environment.GetEnvironmentVariable")
|
||||
}
|
||||
}
|
||||
|
||||
module EnvironmentToUriConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) {
|
||||
src instanceof EnvironmentVariableFlowSource
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(Call c | c.getTarget().(Constructor).getDeclaringType().hasQualifiedName("System.Uri")
|
||||
and sink.asExpr()=c.getArgument(0))
|
||||
}
|
||||
}
|
||||
|
||||
module EnvironmentToUriFlow = DataFlow::Global<EnvironmentToUriConfig>;
|
||||
|
||||
import EnvironmentToUriFlow::PathGraph
|
||||
|
||||
from EnvironmentToUriFlow::PathNode src, EnvironmentToUriFlow::PathNode sink
|
||||
where EnvironmentToUriFlow::flowPath(src, sink)
|
||||
select src.getNode(), src, sink, "This environment variable constructs a 'System.Uri' $@.", sink, "here"
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ The resulting module has an identical signature to the one obtained from ``DataF
|
||||
Flow sources
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The data flow library contains some predefined flow sources. The class ``RemoteFlowSource`` (defined in ``semmle.code.java.dataflow.FlowSources``) represents data flow sources that may be controlled by a remote user, which is useful for finding security problems.
|
||||
The data flow library contains some predefined flow sources. The class ``RemoteFlowSource`` represents data flow sources that may be controlled by a remote user, which is useful for finding security problems.
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
@@ -252,7 +252,7 @@ Exercise 2: Write a query that finds all hard-coded strings used to create a ``u
|
||||
|
||||
Exercise 3: Write a class that represents flow sources from ``os.Getenv(..)``. (`Answer <#exercise-3>`__)
|
||||
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``os.Getenv`` to ``url.URL``. (`Answer <#exercise-4>`__)
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``os.Getenv`` to ``url.URL``. (`Answer <#exercise-4>`__ `Answer as a path query <#path-query-example>`__)
|
||||
|
||||
Answers
|
||||
-------
|
||||
@@ -312,7 +312,7 @@ Exercise 3
|
||||
|
||||
import go
|
||||
|
||||
class GetenvSource extends CallExpr {
|
||||
class GetenvSource extends DataFlow::CallNode {
|
||||
GetenvSource() {
|
||||
exists(Function m | m = this.getTarget() |
|
||||
m.hasQualifiedName("os", "Getenv")
|
||||
@@ -327,7 +327,7 @@ Exercise 4
|
||||
|
||||
import go
|
||||
|
||||
class GetenvSource extends CallExpr {
|
||||
class GetenvSource extends DataFlow::CallNode {
|
||||
GetenvSource() {
|
||||
exists(Function m | m = this.getTarget() |
|
||||
m.hasQualifiedName("os", "Getenv")
|
||||
@@ -351,7 +351,6 @@ Exercise 4
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module GetenvToURLFlow = DataFlow::Global<GetenvToURLConfig>;
|
||||
|
||||
@@ -359,6 +358,56 @@ Exercise 4
|
||||
where GetenvToURLFlow::flow(src, sink)
|
||||
select src, "This environment variable constructs a URL $@.", sink, "here"
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the answer to exercise 4 above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id getenv-to-url
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
class GetenvSource extends DataFlow::CallNode {
|
||||
GetenvSource() {
|
||||
exists(Function m | m = this.getTarget() |
|
||||
m.hasQualifiedName("os", "Getenv")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module GetenvToURLConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof GetenvSource
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(Function urlParse, CallExpr call |
|
||||
(
|
||||
urlParse.hasQualifiedName("url", "Parse") or
|
||||
urlParse.hasQualifiedName("url", "ParseRequestURI")
|
||||
) and
|
||||
call.getTarget() = urlParse and
|
||||
sink.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module GetenvToURLFlow = DataFlow::Global<GetenvToURLConfig>;
|
||||
|
||||
import GetenvToURLFlow::PathGraph
|
||||
|
||||
from GetenvToURLFlow::PathNode src, GetenvToURLFlow::PathNode sink
|
||||
where GetenvToURLFlow::flowPath(src, sink)
|
||||
select src.getNode(), src, sink, "This environment variable constructs a URL $@.", sink, "here"
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ Exercise 2: Write a query that finds all hard-coded strings used to create a ``j
|
||||
|
||||
Exercise 3: Write a class that represents flow sources from ``java.lang.System.getenv(..)``. (`Answer <#exercise-3>`__)
|
||||
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``getenv`` to ``java.net.URL``. (`Answer <#exercise-4>`__)
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from ``getenv`` to ``java.net.URL``. (`Answer <#exercise-4>`__ `Answer as a path query <#path-query-example>`__)
|
||||
|
||||
Answers
|
||||
-------
|
||||
@@ -361,6 +361,54 @@ Exercise 4
|
||||
where GetenvToURLFlow::flow(src, sink)
|
||||
select src, "This environment variable constructs a URL $@.", sink, "here"
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the answer to exercise 4 above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id getenv-to-url
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
class GetenvSource extends DataFlow::ExprNode {
|
||||
GetenvSource() {
|
||||
exists(Method m | m = this.asExpr().(MethodCall).getMethod() |
|
||||
m.hasName("getenv") and
|
||||
m.getDeclaringType() instanceof TypeSystem
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module GetenvToURLConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof GetenvSource
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(Call call |
|
||||
sink.asExpr() = call.getArgument(0) and
|
||||
call.getCallee().(Constructor).getDeclaringType().hasQualifiedName("java.net", "URL")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module GetenvToURLFlow = DataFlow::Global<GetenvToURLConfig>;
|
||||
|
||||
import GetenvToURLFlow::PathGraph
|
||||
|
||||
from GetenvToURLFlow::PathNode src, GetenvToURLFlow::PathNode sink
|
||||
where GetenvToURLFlow::flowPath(src, sink)
|
||||
select src.getNode(), src, sink, "This environment variable constructs a URL $@.", sink, "here"
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@ Exercise 3: Write a class which represents flow sources from the array elements
|
||||
Hint: array indices are properties with numeric names; you can use regular expression matching to check this. (`Answer <#exercise-3>`__)
|
||||
|
||||
Exercise 4: Using the answers from 2 and 3, write a query which finds all global data flow paths from array elements of the result of a call to the ``tagName`` argument to the
|
||||
``createElement`` function. (`Answer <#exercise-4>`__)
|
||||
``createElement`` function. (`Answer <#exercise-4>`__ `Answer as a path query <#path-query-example>`__)
|
||||
|
||||
Answers
|
||||
-------
|
||||
@@ -541,6 +541,48 @@ Exercise 4
|
||||
where HardCodedTagNameFlow::flow(source, sink)
|
||||
select source, sink
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the answer to exercise 4 above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id hard-coded-tag-name
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
class ArrayEntryCallResult extends DataFlow::Node {
|
||||
ArrayEntryCallResult() {
|
||||
exists(DataFlow::CallNode call, string index |
|
||||
this = call.getAPropertyRead(index) and
|
||||
index.regexpMatch("\\d+")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module HardCodedTagNameConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof ArrayEntryCallResult }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = DataFlow::globalVarRef("document").getAMethodCall("createElement").getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
module HardCodedTagNameFlow = DataFlow::Global<HardCodedTagNameConfig>;
|
||||
|
||||
import HardCodedTagNameFlow::PathGraph
|
||||
|
||||
from HardCodedTagNameFlow::PathNode source, HardCodedTagNameFlow::PathNode sink
|
||||
where HardCodedTagNameFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Hard-coded tag name $@.", source, "here"
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
|
||||
@@ -354,11 +354,50 @@ This data flow configuration tracks data flow from environment variables to open
|
||||
select fileOpen, "This call to 'os.open' uses data from $@.",
|
||||
environment, "call to 'os.getenv'"
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the network input example above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id file-system-access-from-remote-input
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
import semmle.python.Concepts
|
||||
|
||||
module RemoteToFileConfiguration implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof RemoteFlowSource
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(FileSystemAccess fa).getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
module RemoteToFileFlow = TaintTracking::Global<RemoteToFileConfiguration>;
|
||||
|
||||
import RemoteToFileFlow::PathGraph
|
||||
|
||||
from RemoteToFileFlow::PathNode input, RemoteToFileFlow::PathNode fileAccess
|
||||
where RemoteToFileFlow::flowPath(input, fileAccess)
|
||||
select fileAccess.getNode(), input, fileAccess, "This file access uses data from $@.",
|
||||
input, "user-controllable input."
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
- `Exploring data flow with path queries <https://docs.github.com/en/code-security/codeql-for-vs-code/getting-started-with-codeql-for-vs-code/exploring-data-flow-with-path-queries>`__ in the GitHub documentation.
|
||||
- `Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__.
|
||||
|
||||
|
||||
.. include:: ../reusables/python-further-reading.rst
|
||||
|
||||
@@ -372,6 +372,43 @@ The following global data-flow query finds calls to ``File.open`` where the file
|
||||
select fileOpen, "This call to 'File.open' uses data from $@.", environment,
|
||||
"an environment variable"
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the taint-tracking example above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id file-system-access-from-remote-input
|
||||
*/
|
||||
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.TaintTracking
|
||||
import codeql.ruby.Concepts
|
||||
import codeql.ruby.dataflow.RemoteFlowSources
|
||||
|
||||
module RemoteToFileConfiguration implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(FileSystemAccess fa).getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
module RemoteToFileFlow = TaintTracking::Global<RemoteToFileConfiguration>;
|
||||
|
||||
import RemoteToFileFlow::PathGraph
|
||||
|
||||
from RemoteToFileFlow::PathNode input, RemoteToFileFlow::PathNode fileAccess
|
||||
where RemoteToFileFlow::flowPath(input, fileAccess)
|
||||
select fileAccess.getNode(), input, fileAccess, "This file access uses data from $@.",
|
||||
input, "user-controllable input."
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
|
||||
@@ -231,6 +231,46 @@ The following global taint-tracking query finds places where a string literal is
|
||||
where ConstantPasswordFlow::flow(sourceNode, sinkNode)
|
||||
select sinkNode, "The value $@ is used as a constant password.", sourceNode, sourceNode.toString()
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the taint-tracking example above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id constant-password
|
||||
*/
|
||||
|
||||
import rust
|
||||
import codeql.rust.dataflow.DataFlow
|
||||
import codeql.rust.dataflow.TaintTracking
|
||||
|
||||
module ConstantPasswordConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node.asExpr().getExpr() instanceof StringLiteralExpr }
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
// any argument going to a parameter called `password`
|
||||
exists(Function f, CallExpr call, int index |
|
||||
call.getArg(index) = node.asExpr().getExpr() and
|
||||
call.getStaticTarget() = f and
|
||||
f.getParam(index).getPat().(IdentPat).getName().getText() = "password"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module ConstantPasswordFlow = TaintTracking::Global<ConstantPasswordConfig>;
|
||||
|
||||
import ConstantPasswordFlow::PathGraph
|
||||
|
||||
from ConstantPasswordFlow::PathNode sourceNode, ConstantPasswordFlow::PathNode sinkNode
|
||||
where ConstantPasswordFlow::flowPath(sourceNode, sinkNode)
|
||||
select sinkNode.getNode(), sourceNode, sinkNode, "The value $@ is used as a constant password.", sourceNode, sourceNode.toString()
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
@@ -278,6 +278,45 @@ The following global taint-tracking query finds places where a value from a remo
|
||||
where SqlInjectionFlow::flow(sourceNode, sinkNode)
|
||||
select sinkNode, "This query depends on a $@.", sourceNode, "user-provided value"
|
||||
|
||||
Path query example
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is the string literal example above, converted into a path query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id sql-injection
|
||||
*/
|
||||
|
||||
import swift
|
||||
import codeql.swift.dataflow.DataFlow
|
||||
import codeql.swift.dataflow.TaintTracking
|
||||
import codeql.swift.dataflow.FlowSources
|
||||
|
||||
module SqlInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(CallExpr call |
|
||||
call.getStaticTarget().(Method).hasQualifiedName("Connection", "execute(_:)") and
|
||||
call.getArgument(0).getExpr() = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module SqlInjectionFlow = TaintTracking::Global<SqlInjectionConfig>;
|
||||
|
||||
import SqlInjectionFlow::PathGraph
|
||||
|
||||
from SqlInjectionFlow::PathNode sourceNode, SqlInjectionFlow::PathNode sinkNode
|
||||
where SqlInjectionFlow::flowPath(sourceNode, sinkNode)
|
||||
select sinkNode.getNode(), sourceNode, sinkNode, "This query depends on a $@.", sourceNode, "user-provided value"
|
||||
|
||||
For more information, see "`Creating path queries <https://codeql.github.com/docs/writing-codeql-queries/creating-path-queries/>`__".
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user