Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions actions/ql/lib/codeql/actions/Ast.qll
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ class Step extends AstNode instanceof StepImpl {

If getIf() { result = super.getIf() }

AstNode getUses() { result = super.getUses() }

StepsContainer getContainer() { result = super.getContainer() }

Step getNextStep() { result = super.getNextStep() }
Expand Down
2 changes: 2 additions & 0 deletions actions/ql/lib/codeql/actions/ast/internal/Ast.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,8 @@ class StepImpl extends AstNodeImpl, TStepNode {
/** Gets the value of the `if` field in this step, if any. */
IfImpl getIf() { result.getNode() = n.lookup("if") }

AstNodeImpl getUses() { result.getNode() = n.lookup("uses") }

/** Gets the Runs or LocalJob that this step is in. */
StepsContainerImpl getContainer() {
result = this.getParentNode().(RunsImpl) or
Expand Down
10 changes: 10 additions & 0 deletions actions/ql/lib/codeql/actions/config/Config.qll
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,13 @@ predicate untrustedGitCommandDataModel(string cmd_regex, string flag) {
predicate untrustedGhCommandDataModel(string cmd_regex, string flag) {
Extensions::untrustedGhCommandDataModel(cmd_regex, flag)
}

/**
* MaD models for permissions needed by actions
* Fields:
* - action: action name
* - permission: permission name
*/
predicate actionsPermissionsDataModel(string action, string permission) {
Extensions::actionsPermissionsDataModel(action, permission)
}
5 changes: 5 additions & 0 deletions actions/ql/lib/codeql/actions/config/ConfigExtensions.qll
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,8 @@ extensible predicate untrustedGitCommandDataModel(string cmd_regex, string flag)
* Holds for gh commands that may introduce untrusted data
*/
extensible predicate untrustedGhCommandDataModel(string cmd_regex, string flag);

/**
* Holds if `action` needs `permission` to run.
*/
extensible predicate actionsPermissionsDataModel(string action, string permission);
37 changes: 37 additions & 0 deletions actions/ql/lib/ext/config/actions_permissions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
extensions:
- addsTo:
pack: codeql/actions-all
extensible: actionsPermissionsDataModel
data:
- ["actions/checkout", "contents: read"]
- ["actions/setup-node", "contents: read"]
- ["actions/setup-python", "contents: read"]
- ["actions/setup-java", "contents: read"]
- ["actions/setup-go", "contents: read"]
- ["actions/setup-dotnet", "contents: read"]
- ["actions/labeler", "contents: read"]
- ["actions/labeler", "pull-requests: write"]
- ["actions/attest", "id-token: write"]
- ["actions/attest", "attestations: write"]
# No permissions needed for actions/add-to-project
- ["actions/dependency-review-action", "contents: read"]
- ["actions/attest-sbom", "id-token: write"]
- ["actions/attest-sbom", "attestations: write"]
- ["actions/stale", "contents: write"]
- ["actions/stale", "issues: write"]
- ["actions/stale", "pull-requests: write"]
- ["actions/attest-build-provenance", "id-token: write"]
- ["actions/attest-build-provenance", "attestations: write"]
- ["actions/jekyll-build-pages", "contents: read"]
- ["actions/jekyll-build-pages", "pages: write"]
- ["actions/jekyll-build-pages", "id-token: write"]
- ["actions/publish-action", "contents: write"]
- ["actions/versions-package-tools", "contents: read"]
- ["actions/versions-package-tools", "actions: read"]
- ["actions/reusable-workflows", "contents: read"]
- ["actions/reusable-workflows", "actions: read"]
# TODO: Add permissions for actions/download-artifact
# TODO: Add permissions for actions/upload-artifact
# TODO: Add permissions for actions/cache


29 changes: 26 additions & 3 deletions actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,36 @@

import actions

from Job job
Step stepInJob(Job job) { result = job.(LocalJob).getAStep() }

bindingset[fullActionSelector]
string versionedAction(string fullActionSelector) {
result = fullActionSelector.substring(0, fullActionSelector.indexOf("@"))
or
not exists(fullActionSelector.indexOf("@")) and
result = fullActionSelector
}

string stepUses(Step step) { result = step.getUses().(ScalarValue).getValue() }

string jobNeedsPersmission(Job job) {
actionsPermissionsDataModel(versionedAction(stepUses(stepInJob(job))), result)
}

string permissionsForJob(Job job) {
result =
"{" + concat(string permission | permission = jobNeedsPersmission(job) | permission, ", ") + "}"
}

from Job job, string permissions
where
not exists(job.getPermissions()) and
not exists(job.getEnclosingWorkflow().getPermissions()) and
// exists a trigger event that is not a workflow_call
exists(Event e |
e = job.getATriggerEvent() and
not e.getName() = "workflow_call"
)
select job, "Actions Job or Workflow does not set permissions"
) and
permissions = permissionsForJob(job)
select job,
"Actions Job or Workflow does not set permissions. A minimal set might be " + permissions
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
on:
workflow_call:
workflow_dispatch:

jobs:
build:
name: Build and test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/jekyll-build-pages


Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
| .github/workflows/perms1.yml:6:5:9:32 | Job: build | Actions Job or Workflow does not set permissions |
| .github/workflows/perms2.yml:6:5:10:2 | Job: build | Actions Job or Workflow does not set permissions |
| .github/workflows/perms5.yml:7:5:10:32 | Job: build | Actions Job or Workflow does not set permissions |
| .github/workflows/perms1.yml:6:5:9:32 | Job: build | Actions Job or Workflow does not set permissions. A minimal set might be {contents: read} |
| .github/workflows/perms2.yml:6:5:10:2 | Job: build | Actions Job or Workflow does not set permissions. A minimal set might be {contents: read} |
| .github/workflows/perms5.yml:7:5:10:32 | Job: build | Actions Job or Workflow does not set permissions. A minimal set might be {contents: read} |
| .github/workflows/perms6.yml:7:5:11:39 | Job: build | Actions Job or Workflow does not set permissions. A minimal set might be {contents: read, id-token: write, pages: write} |
Loading