diff --git a/ql/lib/codeql/actions/Ast.qll b/ql/lib/codeql/actions/Ast.qll index b04694ed568..605f658b263 100644 --- a/ql/lib/codeql/actions/Ast.qll +++ b/ql/lib/codeql/actions/Ast.qll @@ -369,7 +369,10 @@ private string jobsCtxRegex() { private string envCtxRegex() { result = "\\benv\\.([A-Za-z0-9_-]+)\\b" } -private string inputsCtxRegex() { result = "\\binputs\\.([A-Za-z0-9_-]+)\\b" } +private string inputsCtxRegex() { + result = "\\binputs\\.([A-Za-z0-9_-]+)\\b" or + result = "\\bgithub\\.event\\.inputs\\.([A-Za-z0-9_-]+)\\b" +} /** * Holds for an expression accesing the `steps` context. diff --git a/ql/src/Security/CWE-020/CompositeActionSummaries.ql b/ql/src/Security/CWE-020/CompositeActionSummaries.ql index e2843326e74..b451d9d1bda 100644 --- a/ql/src/Security/CWE-020/CompositeActionSummaries.ql +++ b/ql/src/Security/CWE-020/CompositeActionSummaries.ql @@ -31,5 +31,7 @@ module MyFlow = TaintTracking::Global; import MyFlow::PathGraph from MyFlow::PathNode source, MyFlow::PathNode sink -where MyFlow::flowPath(source, sink) +where + MyFlow::flowPath(source, sink) and + source.getNode().getLocation().getFile() = sink.getNode().getLocation().getFile() select sink.getNode(), source, sink, "Summary" diff --git a/ql/src/Security/CWE-020/CompositeActionsSinks.ql b/ql/src/Security/CWE-020/CompositeActionsSinks.ql new file mode 100644 index 00000000000..525307bcc28 --- /dev/null +++ b/ql/src/Security/CWE-020/CompositeActionsSinks.ql @@ -0,0 +1,42 @@ +/** + * @name Composite Action Sinks + * @description Actions passing input variables to expression injection sinks. + * @kind path-problem + * @problem.severity warning + * @security-severity 9.3 + * @precision high + * @id actions/composite-action-sinks + * @tags actions + * model-generator + * external/cwe/cwe-020 + */ + +import actions +import codeql.actions.TaintTracking +import codeql.actions.dataflow.FlowSources +import codeql.actions.dataflow.ExternalFlow + +private class ExpressionInjectionSink extends DataFlow::Node { + ExpressionInjectionSink() { + exists(RunExpr e | e.getScriptExpr() = this.asExpr()) or + externallyDefinedSink(this, "expression-injection") + } +} + +private module MyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(CompositeActionStmt c | c.getInputsStmt().getInputExpr(_) = source.asExpr()) + } + + predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionInjectionSink } +} + +module MyFlow = TaintTracking::Global; + +import MyFlow::PathGraph + +from MyFlow::PathNode source, MyFlow::PathNode sink +where + MyFlow::flowPath(source, sink) and + source.getNode().getLocation().getFile() = sink.getNode().getLocation().getFile() +select sink.getNode(), source, sink, "Sink" diff --git a/ql/src/Security/CWE-020/CompositeActionsSources.ql b/ql/src/Security/CWE-020/CompositeActionsSources.ql index 67adac7dd32..b3eb6d348a8 100644 --- a/ql/src/Security/CWE-020/CompositeActionsSources.ql +++ b/ql/src/Security/CWE-020/CompositeActionsSources.ql @@ -40,5 +40,7 @@ module MyFlow = TaintTracking::Global; import MyFlow::PathGraph from MyFlow::PathNode source, MyFlow::PathNode sink -where MyFlow::flowPath(source, sink) +where + MyFlow::flowPath(source, sink) and + source.getNode().getLocation().getFile() = sink.getNode().getLocation().getFile() select sink.getNode(), source, sink, "Source" diff --git a/ql/src/Security/CWE-020/ReusableWorkflowsSinks.ql b/ql/src/Security/CWE-020/ReusableWorkflowsSinks.ql new file mode 100644 index 00000000000..9317b900158 --- /dev/null +++ b/ql/src/Security/CWE-020/ReusableWorkflowsSinks.ql @@ -0,0 +1,42 @@ +/** + * @name Reusable Workflow Sinks + * @description Reusable Workflows passing parameters to an expression injection sink. + * @kind path-problem + * @problem.severity warning + * @security-severity 9.3 + * @precision high + * @id actions/reusable-wokflow-sinks + * @tags actions + * model-generator + * external/cwe/cwe-020 + */ + +import actions +import codeql.actions.TaintTracking +import codeql.actions.dataflow.FlowSources +import codeql.actions.dataflow.ExternalFlow + +private class ExpressionInjectionSink extends DataFlow::Node { + ExpressionInjectionSink() { + exists(RunExpr e | e.getScriptExpr() = this.asExpr()) or + externallyDefinedSink(this, "expression-injection") + } +} + +private module MyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(ReusableWorkflowStmt w | w.getInputsStmt().getInputExpr(_) = source.asExpr()) + } + + predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionInjectionSink } +} + +module MyFlow = TaintTracking::Global; + +import MyFlow::PathGraph + +from MyFlow::PathNode source, MyFlow::PathNode sink +where + MyFlow::flowPath(source, sink) and + source.getNode().getLocation().getFile() = sink.getNode().getLocation().getFile() +select sink.getNode(), source, sink, "Sink" diff --git a/ql/src/Security/CWE-020/ReusableWorkflowsSources.ql b/ql/src/Security/CWE-020/ReusableWorkflowsSources.ql new file mode 100644 index 00000000000..eeea688b273 --- /dev/null +++ b/ql/src/Security/CWE-020/ReusableWorkflowsSources.ql @@ -0,0 +1,46 @@ +/** + * @name Reusable Workflow Sources + * @description Reusable Workflow that pass user-controlled data to their output variables. + * @kind path-problem + * @problem.severity warning + * @security-severity 9.3 + * @precision high + * @id actions/reusable-workflow-sources + * @tags actions + * model-generator + * external/cwe/cwe-020 + */ + +import actions +import codeql.actions.TaintTracking +import codeql.actions.dataflow.FlowSources +import codeql.actions.dataflow.ExternalFlow + +private module MyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource and + not source instanceof DataFlow::ParameterNode and + exists(ReusableWorkflowStmt w | w.getAChildNode*() = source.asExpr()) + } + + predicate isSink(DataFlow::Node sink) { + exists(ReusableWorkflowStmt w | w.getOutputsStmt().getOutputExpr(_) = sink.asExpr()) + } + + predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet set) { + allowImplicitRead(node, set) + or + isSink(node) and + set instanceof DataFlow::FieldContent + } +} + +module MyFlow = TaintTracking::Global; + +import MyFlow::PathGraph + +from MyFlow::PathNode source, MyFlow::PathNode sink +where + MyFlow::flowPath(source, sink) and + source.getNode().getLocation().getFile() = sink.getNode().getLocation().getFile() +select sink.getNode(), source, sink, "Source" diff --git a/ql/src/Security/CWE-020/ReusableWorkflowsSummaries.ql b/ql/src/Security/CWE-020/ReusableWorkflowsSummaries.ql new file mode 100644 index 00000000000..3949488e129 --- /dev/null +++ b/ql/src/Security/CWE-020/ReusableWorkflowsSummaries.ql @@ -0,0 +1,37 @@ +/** + * @name Reusable Workflows Summaries + * @description Reusable workflow that pass user-controlled data to their output variables. + * @kind path-problem + * @problem.severity warning + * @security-severity 9.3 + * @precision high + * @id actions/reusable-workflow-summaries + * @tags actions + * model-generator + * external/cwe/cwe-020 + */ + +import actions +import codeql.actions.TaintTracking +import codeql.actions.dataflow.FlowSources +import codeql.actions.dataflow.ExternalFlow + +private module MyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(ReusableWorkflowStmt w | w.getInputsStmt().getInputExpr(_) = source.asExpr()) + } + + predicate isSink(DataFlow::Node sink) { + exists(ReusableWorkflowStmt w | w.getOutputsStmt().getOutputExpr(_) = sink.asExpr()) + } +} + +module MyFlow = TaintTracking::Global; + +import MyFlow::PathGraph + +from MyFlow::PathNode source, MyFlow::PathNode sink +where + MyFlow::flowPath(source, sink) and + source.getNode().getLocation().getFile() = sink.getNode().getLocation().getFile() +select sink.getNode(), source, sink, "Summary"