mirror of
https://github.com/github/codeql.git
synced 2026-01-10 05:00:29 +01:00
Merge pull request #47 from github/poisonable_config
Move configuration to MaD files
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
private import codeql.actions.ast.internal.Yaml
|
||||
private import codeql.Locations
|
||||
private import codeql.actions.Helper
|
||||
private import codeql.actions.dataflow.ExternalFlow
|
||||
private import codeql.actions.config.Config
|
||||
|
||||
/**
|
||||
* Gets the length of each line in the StringValue .
|
||||
|
||||
74
ql/lib/codeql/actions/config/Config.qll
Normal file
74
ql/lib/codeql/actions/config/Config.qll
Normal file
@@ -0,0 +1,74 @@
|
||||
import ConfigExtensions as Extensions
|
||||
|
||||
/**
|
||||
* MaD models for workflow details
|
||||
* Fields:
|
||||
* - path: Path to the workflow file
|
||||
* - trigger: Trigger for the workflow
|
||||
* - job: Job name
|
||||
* - secrets_source: Source of secrets
|
||||
* - permissions: Permissions for the workflow
|
||||
* - runner: Runner info for the workflow
|
||||
*/
|
||||
predicate workflowDataModel(
|
||||
string path, string trigger, string job, string secrets_source, string permissions, string runner
|
||||
) {
|
||||
Extensions::workflowDataModel(path, trigger, job, secrets_source, permissions, runner)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for repository details
|
||||
* Fields:
|
||||
* - visibility: Visibility of the repository
|
||||
* - default_branch_name: Default branch name
|
||||
*/
|
||||
predicate repositoryDataModel(string visibility, string default_branch_name) {
|
||||
Extensions::repositoryDataModel(visibility, default_branch_name)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for context/trigger mapping
|
||||
* Fields:
|
||||
* - trigger: Trigger for the workflow
|
||||
* - context_prefix: Prefix for the context
|
||||
*/
|
||||
predicate contextTriggerDataModel(string trigger, string context_prefix) {
|
||||
Extensions::contextTriggerDataModel(trigger, context_prefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for externally triggerable events
|
||||
* Fields:
|
||||
* - event: Event name
|
||||
*/
|
||||
predicate externallyTriggerableEventsDataModel(string event) {
|
||||
Extensions::externallyTriggerableEventsDataModel(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for poisonable commands
|
||||
* Fields:
|
||||
* - regexp: Regular expression for matching poisonable commands
|
||||
*/
|
||||
predicate poisonableCommandsDataModel(string regexp) {
|
||||
Extensions::poisonableCommandsDataModel(regexp)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for poisonable local scripts
|
||||
* Fields:
|
||||
* - regexp: Regular expression for matching poisonable local scripts
|
||||
* - group: Script capture group number for the regular expression
|
||||
*/
|
||||
predicate poisonableLocalScriptsDataModel(string regexp, int group) {
|
||||
Extensions::poisonableLocalScriptsDataModel(regexp, group)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for poisonable actions
|
||||
* Fields:
|
||||
* - action: action name
|
||||
*/
|
||||
predicate poisonableActionsDataModel(string action) {
|
||||
Extensions::poisonableActionsDataModel(action)
|
||||
}
|
||||
41
ql/lib/codeql/actions/config/ConfigExtensions.qll
Normal file
41
ql/lib/codeql/actions/config/ConfigExtensions.qll
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* This module provides extensible predicates for defining MaD models.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds if workflow data model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate workflowDataModel(
|
||||
string path, string trigger, string job, string secrets_source, string permissions, string runner
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if repository data model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate repositoryDataModel(string visibility, string default_branch_name);
|
||||
|
||||
/**
|
||||
* Holds if a context expression starting with context_prefix is available for a given trigger.
|
||||
*/
|
||||
extensible predicate contextTriggerDataModel(string trigger, string context_prefix);
|
||||
|
||||
/**
|
||||
* Holds if a given trigger event can be fired by an external actor.
|
||||
*/
|
||||
extensible predicate externallyTriggerableEventsDataModel(string event);
|
||||
|
||||
/**
|
||||
* Holds for strings that match poisonable commands.
|
||||
*/
|
||||
extensible predicate poisonableCommandsDataModel(string regexp);
|
||||
|
||||
/**
|
||||
* Holds for strings that match poisonable local scripts.
|
||||
*/
|
||||
extensible predicate poisonableLocalScriptsDataModel(string regexp, int group);
|
||||
|
||||
/**
|
||||
* Holds for actions that can be poisoned through local files.
|
||||
*/
|
||||
extensible predicate poisonableActionsDataModel(string action);
|
||||
|
||||
@@ -2,51 +2,6 @@ private import internal.ExternalFlowExtensions as Extensions
|
||||
private import codeql.actions.DataFlow
|
||||
private import actions
|
||||
|
||||
/**
|
||||
* MaD models for workflow details
|
||||
* Fields:
|
||||
* - path: Path to the workflow file
|
||||
* - trigger: Trigger for the workflow
|
||||
* - job: Job name
|
||||
* - secrets_source: Source of secrets
|
||||
* - permissions: Permissions for the workflow
|
||||
* - runner: Runner info for the workflow
|
||||
*/
|
||||
predicate workflowDataModel(
|
||||
string path, string trigger, string job, string secrets_source, string permissions, string runner
|
||||
) {
|
||||
Extensions::workflowDataModel(path, trigger, job, secrets_source, permissions, runner)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for repository details
|
||||
* Fields:
|
||||
* - visibility: Visibility of the repository
|
||||
* - default_branch_name: Default branch name
|
||||
*/
|
||||
predicate repositoryDataModel(string visibility, string default_branch_name) {
|
||||
Extensions::repositoryDataModel(visibility, default_branch_name)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for context/trigger mapping
|
||||
* Fields:
|
||||
* - trigger: Trigger for the workflow
|
||||
* - context_prefix: Prefix for the context
|
||||
*/
|
||||
predicate contextTriggerDataModel(string trigger, string context_prefix) {
|
||||
Extensions::contextTriggerDataModel(trigger, context_prefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD models for externally triggerable events
|
||||
* Fields:
|
||||
* - event: Event name
|
||||
*/
|
||||
predicate externallyTriggerableEventsDataModel(string event) {
|
||||
Extensions::externallyTriggerableEventsDataModel(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* MaD sources
|
||||
* Fields:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
private import codeql.actions.dataflow.ExternalFlow
|
||||
private import codeql.actions.security.ArtifactPoisoningQuery
|
||||
private import codeql.actions.config.Config
|
||||
private import codeql.actions.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A data flow source.
|
||||
|
||||
@@ -22,25 +22,3 @@ extensible predicate actionsSummaryModel(
|
||||
extensible predicate actionsSinkModel(
|
||||
string action, string version, string input, string kind, string provenance
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if workflow data model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate workflowDataModel(
|
||||
string path, string trigger, string job, string secrets_source, string permissions, string runner
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if repository data model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate repositoryDataModel(string visibility, string default_branch_name);
|
||||
|
||||
/**
|
||||
* Holds if a context expression starting with context_prefix is available for a given trigger.
|
||||
*/
|
||||
extensible predicate contextTriggerDataModel(string trigger, string context_prefix);
|
||||
|
||||
/**
|
||||
* Holds if a given trigger event can be fired by an external actor.
|
||||
*/
|
||||
extensible predicate externallyTriggerableEventsDataModel(string event);
|
||||
|
||||
@@ -254,8 +254,8 @@ class ArtifactPoisoningSink extends DataFlow::Node {
|
||||
poisonable.(UsesStep) = this.asExpr()
|
||||
) and
|
||||
(
|
||||
not poisonable instanceof LocalCommandExecutionRunStep or
|
||||
poisonable.(LocalCommandExecutionRunStep).getCommand().matches(download.getPath() + "%")
|
||||
not poisonable instanceof LocalScriptExecutionRunStep or
|
||||
poisonable.(LocalScriptExecutionRunStep).getCommand().matches(download.getPath() + "%")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import actions
|
||||
import codeql.actions.dataflow.ExternalFlow
|
||||
import codeql.actions.config.Config
|
||||
|
||||
string defaultBranchTriggerEvent() {
|
||||
result =
|
||||
|
||||
@@ -1,67 +1,35 @@
|
||||
import actions
|
||||
import codeql.actions.config.Config
|
||||
|
||||
abstract class PoisonableStep extends Step { }
|
||||
|
||||
// source: https://github.com/boostsecurityio/poutine/blob/main/opa/rego/rules/untrusted_checkout_exec.rego#L16
|
||||
private string dangerousActions() {
|
||||
result =
|
||||
[
|
||||
"pre-commit/action", "oxsecurity/megalinter", "bridgecrewio/checkov-action",
|
||||
"ruby/setup-ruby", "actions/jekyll-build-pages"
|
||||
]
|
||||
exists(string action |
|
||||
poisonableActionsDataModel(action) and
|
||||
result = action
|
||||
)
|
||||
}
|
||||
|
||||
class DangerousActionUsesStep extends PoisonableStep, UsesStep {
|
||||
DangerousActionUsesStep() { this.getCallee() = dangerousActions() }
|
||||
}
|
||||
|
||||
// source: https://github.com/boostsecurityio/poutine/blob/main/opa/rego/rules/untrusted_checkout_exec.rego#L23
|
||||
private string dangerousCommands() {
|
||||
result =
|
||||
[
|
||||
"npm i(nstall)?(\\b|$)", "npm run ", "yarn ", "npm ci(\\b|$)", "make ", "terraform plan",
|
||||
"terraform apply", "gomplate ", "pre-commit run", "pre-commit install", "go generate",
|
||||
"msbuild ", "mvn ", "gradle ", "bundle install", "bundle exec ", "^ant ", "mkdocs build",
|
||||
"pytest", "pip install -r ", "pip install --requirement", "java -jar ", "poetry install",
|
||||
"poetry run", "cargo "
|
||||
]
|
||||
}
|
||||
|
||||
class BuildRunStep extends PoisonableStep, Run {
|
||||
BuildRunStep() {
|
||||
exists(
|
||||
this.getScript().splitAt("\n").trim().regexpFind("([^a-z]|^)" + dangerousCommands(), _, _)
|
||||
class PoisonableCommandStep extends PoisonableStep, Run {
|
||||
PoisonableCommandStep() {
|
||||
exists(string regexp |
|
||||
poisonableCommandsDataModel(regexp) and
|
||||
exists(this.getScript().splitAt("\n").trim().regexpFind("([^a-z]|^)" + regexp, _, _))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[cmdRegexp]
|
||||
string wrapLocalCmd(string cmdRegexp) { result = "(^|;\\s*|\\s+)" + cmdRegexp + "(\\s+|;|$)" }
|
||||
|
||||
class LocalCommandExecutionRunStep extends PoisonableStep, Run {
|
||||
class LocalScriptExecutionRunStep extends PoisonableStep, Run {
|
||||
string cmd;
|
||||
|
||||
LocalCommandExecutionRunStep() {
|
||||
// Heuristic:
|
||||
exists(string line | line = this.getScript().splitAt("\n").trim() |
|
||||
// ./xxxx
|
||||
// TODO: It could also be in the form of `dir/cmd`
|
||||
cmd = line.regexpCapture(wrapLocalCmd("\\.\\/(.*)"), 2)
|
||||
or
|
||||
// sh xxxx
|
||||
cmd = line.regexpCapture(wrapLocalCmd("(ba|z|fi)?sh\\s+(.*)"), 3)
|
||||
or
|
||||
// node xxxx.js
|
||||
cmd = line.regexpCapture(wrapLocalCmd("node\\s+(.*)(\\.js|\\.ts)"), 2)
|
||||
or
|
||||
// python xxxx.py
|
||||
cmd = line.regexpCapture(wrapLocalCmd("python\\s+(.*)\\.py"), 2)
|
||||
or
|
||||
// ruby xxxx.rb
|
||||
cmd = line.regexpCapture(wrapLocalCmd("ruby\\s+(.*)\\.rb"), 2)
|
||||
or
|
||||
// go xxxx.go
|
||||
cmd = line.regexpCapture(wrapLocalCmd("go\\s+(.*)\\.go"), 2)
|
||||
LocalScriptExecutionRunStep() {
|
||||
exists(string line, string regexp, int group | line = this.getScript().splitAt("\n").trim() |
|
||||
poisonableLocalScriptsDataModel(regexp, group) and
|
||||
cmd = line.regexpCapture(regexp, group)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import actions
|
||||
import codeql.actions.dataflow.ExternalFlow
|
||||
import codeql.actions.config.Config
|
||||
|
||||
bindingset[runner]
|
||||
predicate isGithubHostedRunner(string runner) {
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: repositoryDataModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: workflowDataModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: contextTriggerDataModel
|
||||
@@ -54,19 +46,4 @@ extensions:
|
||||
- ["workflow_call", "github.event.review"]
|
||||
- ["workflow_call", "github.event.workflow"]
|
||||
- ["workflow_call", "github.event.workflow_run"]
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: externallyTriggerableEventsDataModel
|
||||
data:
|
||||
- ["discussion"]
|
||||
- ["discussion_comment"]
|
||||
- ["fork"]
|
||||
- ["issue_comment"]
|
||||
- ["issues"]
|
||||
- ["pull_request"]
|
||||
- ["pull_request_comment"]
|
||||
- ["pull_request_review"]
|
||||
- ["pull_request_review_comment"]
|
||||
- ["pull_request_target"]
|
||||
- ["workflow_run"] # depending on trigger workflow
|
||||
- ["workflow_call"] # depending on caller
|
||||
|
||||
18
ql/lib/ext/config/externally_triggereable_events.yml
Normal file
18
ql/lib/ext/config/externally_triggereable_events.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: externallyTriggerableEventsDataModel
|
||||
data:
|
||||
- ["discussion"]
|
||||
- ["discussion_comment"]
|
||||
- ["fork"]
|
||||
- ["issue_comment"]
|
||||
- ["issues"]
|
||||
- ["pull_request"]
|
||||
- ["pull_request_comment"]
|
||||
- ["pull_request_review"]
|
||||
- ["pull_request_review_comment"]
|
||||
- ["pull_request_target"]
|
||||
- ["workflow_run"] # depending on trigger workflow
|
||||
- ["workflow_call"] # depending on caller
|
||||
|
||||
55
ql/lib/ext/config/poisonable_steps.yml
Normal file
55
ql/lib/ext/config/poisonable_steps.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: poisonableActionsDataModel
|
||||
# source: https://github.com/boostsecurityio/poutine/blob/main/opa/rego/rules/untrusted_checkout_exec.rego#L16
|
||||
# source: https://boostsecurityio.github.io/lotp/
|
||||
data:
|
||||
- ["pre-commit/action"]
|
||||
- ["oxsecurity/megalinter"]
|
||||
- ["bridgecrewio/checkov-action"]
|
||||
- ["ruby/setup-ruby"]
|
||||
- ["actions/jekyll-build-pages"]
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: poisonableCommandsDataModel
|
||||
# source: https://github.com/boostsecurityio/poutine/blob/main/opa/rego/rules/untrusted_checkout_exec.rego#L23
|
||||
# source: https://boostsecurityio.github.io/lotp/
|
||||
data:
|
||||
- ["ant "]
|
||||
- ["bundle install"]
|
||||
- ["bundle exec "]
|
||||
- ["cargo "]
|
||||
- ["go generate"]
|
||||
- ["gomplate "]
|
||||
- ["gradle "]
|
||||
- ["java -jar "]
|
||||
- ["make "]
|
||||
- ["mkdocs build"]
|
||||
- ["msbuild "]
|
||||
- ["mvn "]
|
||||
- ["npm i(nstall)?(\\b|$)"]
|
||||
- ["npm run "]
|
||||
- ["npm ci(\\b|$)"]
|
||||
- ["pip install -r "]
|
||||
- ["pip install --requirement"]
|
||||
- ["poetry install"]
|
||||
- ["poetry run"]
|
||||
- ["pre-commit run"]
|
||||
- ["pre-commit install"]
|
||||
- ["pytest"]
|
||||
- ["terraform plan"]
|
||||
- ["terraform apply"]
|
||||
- ["yarn "]
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: poisonableLocalScriptsDataModel
|
||||
data:
|
||||
# TODO: It could also be in the form of `dir/cmd`
|
||||
- ["(^|;\\s*|\\s+)(\\.\\/)(.*)(\\s+|;|$)", 3]
|
||||
- ["(^|;\\s*|\\s+)(source|sh|bash|zsh|fish)\\s+(.*)(\\s+|;|$)", 3]
|
||||
- ["(^|;\\s*|\\s+)(node)\\s+(.*)(\\.js|\\.ts)(\\s+|;|$)", 3]
|
||||
- ["(^|;\\s*|\\s+)(python)\\s+(.*)\\.py(\\s+|;|$)", 3]
|
||||
- ["(^|;\\s*|\\s+)(ruby)\\s+(.*)\\.rb(\\s+|;|$)", 3]
|
||||
- ["(^|;\\s*|\\s+)(go)\\s+(.*)\\.go(\\s+|;|$)", 3]
|
||||
|
||||
9
ql/lib/ext/config/workflow_runtime_data.yml
Normal file
9
ql/lib/ext/config/workflow_runtime_data.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: repositoryDataModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: github/actions-all
|
||||
extensible: workflowDataModel
|
||||
data: []
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user