{"id":22975077,"url":"https://github.com/synacktiv/octoscan","last_synced_at":"2026-02-08T14:34:47.503Z","repository":{"id":245291214,"uuid":"810395029","full_name":"synacktiv/octoscan","owner":"synacktiv","description":"Octoscan is a static vulnerability scanner for GitHub action workflows.","archived":false,"fork":false,"pushed_at":"2025-10-28T10:51:52.000Z","size":416,"stargazers_count":228,"open_issues_count":9,"forks_count":18,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-10-28T12:27:48.303Z","etag":null,"topics":["cicd","exploit","github","github-actions","vulnerability"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/synacktiv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-06-04T15:57:21.000Z","updated_at":"2025-10-28T10:49:00.000Z","dependencies_parsed_at":"2024-06-21T06:23:58.125Z","dependency_job_id":"b80e3745-4297-45f8-bb3c-39bb18cc9a64","html_url":"https://github.com/synacktiv/octoscan","commit_stats":null,"previous_names":["synacktiv/octoscan"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/synacktiv/octoscan","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synacktiv%2Foctoscan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synacktiv%2Foctoscan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synacktiv%2Foctoscan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synacktiv%2Foctoscan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/synacktiv","download_url":"https://codeload.github.com/synacktiv/octoscan/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synacktiv%2Foctoscan/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29233308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-08T14:18:14.570Z","status":"ssl_error","status_checked_at":"2026-02-08T14:18:14.071Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cicd","exploit","github","github-actions","vulnerability"],"created_at":"2024-12-15T00:21:14.812Z","updated_at":"2026-02-08T14:34:47.465Z","avatar_url":"https://github.com/synacktiv.png","language":"Go","readme":"\u003cdiv align=\"center\"\u003e\n\t:octocat:\n\u003c/div\u003e\n\u003ch1 align=\"center\"\u003e\n  octoscan\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n   Octoscan is a static vulnerability scanner for GitHub action workflows.\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"img/dependabot.png\"/\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n- [Usage](#usage)\n\t- [download remote workflows](#download-remote-workflows)\n\t- [analyze](#analyze)\n\t- [GitHub action](#github-action)\n- [Rules](#rules)\n\t- [dangerous-checkout](#dangerous-checkout)\n\t- [dangerous-action](#dangerous-action)\n\t- [dangerous-write](#dangerous-write)\n\t- [expression-injection](#expression-injection)\n\t- [runner-label](#runner-label)\n\t- [repo-jacking](#repo-jacking)\n\t- [unsecure-commands](#unsecure-commands)\n\t- [bot-check](#bot-check)\n\t- [known-vulnerability](#known-vulnerability)\n\t- [dangerous-artefact](#dangerous-artefact)\n\t- [credentials](#credentials)\n\t- [shellcheck](#shellcheck)\n\t- [local-action](#local-action)\n\t- [oidc-action](#oidc-action)\n- [Credits](#credits)\n- [Resources](#resources)\n\n## Installation\n\n```\n$ go mod tidy\n$ go build\n```\n\nOr with docker:\n\n```\n$ docker pull ghcr.io/synacktiv/octoscan:latest\n```\n\n## Usage\n\n### download remote workflows\n\nOctoscan can be run against a local git repository or you can download all the workflows with the `dl` action:\n```sh\n$ octoscan dl -h  \nOctoscan.\n\nUsage:\n\toctoscan dl [options] --org \u003corg\u003e [--repo \u003crepo\u003e --token \u003cpat\u003e --default-branch --max-branches \u003cnum\u003e --path \u003cpath\u003e --output-dir \u003cdir\u003e --include-archives]\n\nOptions:\n\t-h, --help  \t\t\t\t\t\tShow help\n\t-d, --debug  \t\t\t\t\t\tDebug output\n\t--verbose  \t\t\t\t\t\tVerbose output\n\t--org \u003corg\u003e  \t\t\t\t\t\tOrganizations to target\n\t--repo \u003crepo\u003e  \t\t\t\t\t\tRepository to target\n\t--token \u003cpat\u003e  \t\t\t\t\t\tGHP to authenticate to GitHub\n\t--default-branch  \t\t\t\t\tOnly download workflows from the default branch\n\t--max-branches \u003cnum\u003e  \t\t\t\t\tLimit the number of branches to download\n\t--path \u003cpath\u003e  \t\t\t\t\t\tGitHub file path to download [default: .github/workflows]\n\t--output-dir \u003cdir\u003e  \t\t\t\t\tOutput dir where to download files [default: octoscan-output]\n\t--include-archives  \t\t\t\t\tAlso download archived repositories\n```\n\n```sh\n./octoscan dl --token ghp_\u003ctoken\u003e --org apache --repo incubator-answer\n```\n\n### analyze\n\nIf you don't know what to run just run this:\n```sh\n./octoscan scan path/to/repos/ --disable-rules shellcheck,local-action --filter-triggers external\n```\n\nIt will reduce false positives and give the most interesting results.\n\nIf you have downloaded the workflows with the [dl](#download-remote-workflows) command you might have duplicated workflows since by default octoscan will download all the workflows of all the branches. To delete duplicated workflows and speed up the analysis you can use the `fdupes` command before running the analysis:\n\n```sh\nfdupes -n -r -N -d path/to/repo\n```\n\n\n```sh\n$ octoscan scan -h\noctoscan\n\nUsage:\n\toctoscan scan [options] --list-rules\n\toctoscan scan [options] \u003ctarget\u003e\n\toctoscan scan [options] \u003ctarget\u003e [--debug-rules --filter-triggers=\u003ctriggers\u003e --filter-run --ignore=\u003cpattern\u003e ((--disable-rules | --enable-rules ) \u003crules\u003e) --config-file \u003cconfig\u003e]\n\nOptions:\n\t-h, --help\n\t-v, --version\n\t-d, --debug\n\t--verbose\n\t--format \u003cformat\u003e  \t\t\t\tOutput format, json, sarif or custom template to format error messages in Go template syntax. See https://github.com/rhysd/actionlint/tree/main/docs/usage.md#format\n\t--oneline \t\t\t\t\tUse one line per one error. Useful for reading error messages from programs\n\nArgs:\n\t\u003ctarget\u003e\t\t\t\t\tTarget File or directory to scan\n\t--filter-triggers \u003ctriggers\u003e\t\t\tScan workflows with specific triggers (comma separated list: \"push,pull_request_target\" or pre-configured: external/allnopr)\n\t--filter-run\t\t\t\t\tSearch for expression injection only in run shell scripts.\n\t--ignore \u003cpattern\u003e\t\t\t\tRegular expression matching to error messages you want to ignore.\n\t--disable-rules \u003crules\u003e\t\t\t\tDisable specific rules. Split on \",\"\n\t--enable-rules \u003crules\u003e\t\t\t\tEnable specific rules, this will disable all other rules. Split on \",\"\n\t--debug-rules\t\t\t\t\tEnable debug rules.\n\t--config-file \u003cconfig\u003e\t\t\t\tConfig file.\n\nExamples:\n\t$ octoscan scan ci.yml --disable-rules shellcheck,local-action --filter-triggers external\n```\n\n### GitHub action\n\nThis tool can also be used directly as a GitHub action to scan your repository on `push`/`pull_request` events. For more information please check [this repository](https://github.com/synacktiv/action-octoscan).\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/synacktiv/action-octoscan/blob/main/img/action-octoscan.png\"/\u003e\n\u003c/div\u003e\n\n## Rules\n\nThe complete list of rules can be found with this command:\n```\n$ octoscan scan --list-rules  \n2024/08/07 16:50:48 [INFO] Available rules\n- shellcheck\n\tChecks for shell script sources in \"run:\" using shellcheck\n- credentials\n\tChecks for credentials in \"services:\" configuration\n- dangerous-action\n\tCheck for dangerous actions.\n- dangerous-checkout\n\tCheck for dangerous checkout.\n- expression-injection\n\tCheck for expression injection.\n- dangerous-write\n\tCheck for dangerous write operation on $GITHUB_OUTPUT or $GITHUB_ENV.\n- local-action\n\tCheck for local actions.\n- runner-label\n\tChecks for GitHub-hosted and preset self-hosted runner labels in \"runs-on:\"\n- unsecure-commands\n\tCheck 'ACTIONS_ALLOW_UNSECURE_COMMANDS' env variable.\n- known-vulnerability\n\tCheck for known vulnerabilities.\n- bot-check\n\tCheck for if statements that are based on a bot identity.\n- dangerous-artefact\n\tCheck for workflow that upload artefacts containing sensitive files.\n- debug-external-trigger\n\tCheck for workflow that can be externally triggered.\n- debug-artefacts\n\tCheck for workflow that upload artefacts.\n- debug-js-exec\n\tCheck for workflow that execute system commands in JS scripts.\n- debug-oidc-action\n\tCheck for OIDC actions.\n- repo-jacking\n\tVerify that external actions are pointing to a valid GitHub user or organization.\n```\n\n### dangerous-checkout\n\nTriggers like `workflow_run` or `pull_request_target` run in a privileged context, as they have read access to secrets and potentially have write access on the targeted repository. Performing an explicit checkout on the untrusted code will result in the attacker code being downloaded in such context.\n\n![excalidraw](img/excalidraw.png)\n\n#### examples\n\n- [FreeRDP](https://github.com/FreeRDP/FreeRDP/pull/10209)\n- [Excalidraw](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n- [AutoGPT](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n- [Cypress](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n- [Apache Doris](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n- [Angular](https://github.com/angular/angular/blob/6b20561e1d6810e867c0ee7692d9fae64426a876/.github/workflows/ci-privileged.yml#L4)\n\n### dangerous-action\n\nThis rules warn the user if a dangerous action is used. It's mainly focused on untrusted artifacts.\n\nIt is common practice to use artifacts to pass data between different workflows. We often encounter this with the `workflow_run` trigger where the triggering workflow will prepare some data that will then be sent to the triggered workflow. Given the untrusted nature of this artifact data, it is crucial to treat it with caution and recognize it as a potential threat. The vulnerability arises from the fact that external entities, such as malicious actors, can influence the content of the artifact data.\n\n![ant-design](img/ant-design.png)\n\n\n#### examples\n\n- [ant-design](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n- [Swagger-editor](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)\n- [Firebase](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)\n- [Firebase](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability-0)\n- [Rust](https://www.legitsecurity.com/blog/artifact-poisoning-vulnerability-discovered-in-rust)\n\n### dangerous-write\n\nGitHub will create default environment variables that can be used inside every step in a workflow. The `GITHUB_ENV` and `GITHUB_OUTPUT` variables are particularly interesting. It is possible to define environment variable in a step and to use this variable in another one. This can be done by writing it to the associated variable variable. If a user can control the content of the variable that is being set it can lead to arbitrary code execution.\n\n![swagger](img/swagger.png)\n\n#### examples\n\n- [Swagger-editor](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)\n- [dgraph-io/badger](https://github.com/dgraph-io/badger/blob/6acc8e801739f6702b8d95f462b8d450b9a0455b/.github/workflows/ci-badger-tests-coverage.yml#L17-L18)\n- [Firebase](https://www.legitsecurity.com/blog/github-privilege-escalation-vulnerability-0)\n- [microsoft/vscode-github-triage-actions](https://bugs.chromium.org/p/project-zero/issues/detail?id=2070)\n\n### expression-injection\n\nEach workflow trigger comes with an associated GitHub context, offering comprehensive information about the event that initiated it. This includes details about the user who triggered the event, the branch name, and other relevant contextual information. Certain components of this event data, such as the base repository name, or pull request number, cannot be manipulated or exploited for injection by the user who initiated the event (e.g., in the case of a pull request). This ensures a level of control and security over the information provided by the GitHub context during workflow execution.\n\nHowever, some elements can be controlled by an attacker and should be sanitized before being used. Here is the list of such elements:\n- `github.event.issue.title`\n- `github.event.issue.body`\n- `github.event.pull_request.title`\n- `github.event.pull_request.body`\n- `github.event.comment.body`\n- `github.event.review.body`\n- `github.event.pages.*.page_name`\n- `github.event.commits.*.message`\n- `github.event.head_commit.message`\n- `github.event.head_commit.author.email`\n- `github.event.head_commit.author.name`\n- `github.event.commits.*.author.email`\n- `github.event.commits.*.author.name`\n- `github.event.pull_request.head.ref`\n- `github.event.pull_request.head.label`\n- `github.event.pull_request.head.repo.default_branch`\n- `github.head_ref`\n- `env.*`\n- `steps.*.outputs.*`\n- `needs.*.outputs.*`\n\n![autogpt](img/autogpt.png)\n\n#### examples\n\n- [AutoGPT](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n- [microsoft/generative-ai-for-beginners](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n\n### runner-label\n\nGitHub offers the possibility to host your own runners and customize the environment used to run jobs in workflows. These runners are called self-hosted.\n\nThere exists two types of self-hosted runners, ephemeral and non-ephemeral ones. By default, the runners are non-ephemeral, meaning the environment used by the runner is not cleaned after a job completes. If attackers manages to execute code on a non-ephemeral runner, they could backdoor it by adding a process in the background and steal sensitive secrets. These kinds of runners are thus really sensitive.\n\n![excalidraw](img/haskell.png)\n\nNon-ephemeral runners can be identified by looking at run logs. A tool called [gato](https://github.com/praetorian-inc/gato) can be used to automate this process.\n\n#### examples\n\n- [Haskell](https://www.synacktiv.com/publications/github-actions-exploitation-self-hosted-runners)\n- [lovell/Sharp](https://www.synacktiv.com/publications/github-actions-exploitation-self-hosted-runners)\n- [WasmEdge](https://www.synacktiv.com/publications/github-actions-exploitation-self-hosted-runner)\n- [Scroll](https://www.synacktiv.com/publications/github-actions-exploitation-self-hosted-runners)\n- [Akash Network](https://www.synacktiv.com/publications/github-actions-exploitation-self-hosted-runners)\n- [actions/runner-images](https://adnanthekhan.com/2023/12/20/one-supply-chain-attack-to-rule-them-all)\n- [tensorflow](https://www.praetorian.com/blog/tensorflow-supply-chain-compromise-via-self-hosted-runner-attack/)\n- [pytorch](https://johnstawinski.com/2024/01/11/playing-with-fire-how-we-executed-a-critical-supply-chain-attack-on-pytorch/)\n\n\n### repo-jacking\n\nThe repo jacking vulnerability was [presented](https://media.defcon.org/DEF%20CON%2031/DEF%20CON%2031%20presentations/Asi%20Greenholts%20-%20The%20GitHub%20Actions%20Worm%20Compromising%20GitHub%20repositories%20through%20the%20Actions%20dependency%20tree.pdf) at DEFCON 31 by Asi Greenholts. This vulnerability occurs when a GitHub action is referencing an action on a non-existing GitHub organization or user.\n\n![AcalaNetwork](img/AcalaNetwork.png)\n\nNote that this rule needs internet to check whether the attack is possible or not. All other checks are performed offline.\n\n#### examples\n\n- [Azure/bicep-registry-modules](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)\n- [HangfireIO/Hangfire](https://www.paloaltonetworks.com/blog/prisma-cloud/github-actions-worm-dependencies/)\n\n\n### unsecure-commands\n\nActions possess the capability to interact with the runner machine, enabling them to set environment variables, define output values for use by other actions, incorporate debug messages into output logs, and perform various other tasks. However, before 2020, it was possible to control environment variables by writing data to `STDOUT` like this:\n\n```yaml\nrun: |\n   echo \"##[set-env name=ENV_NAME;]value\"\n   # or\n   echo \"echo \"::set-env name=ENV_NAME::value\"\n```\n\nThe implemented workflow commands were inherently insecure due to the common practice of logging to STDOUT. This vulnerability opened avenues for potential attacks, allowing malicious payloads to be easily injected and trigger the `set-env` command. The ability to modify environment variables introduced multiple paths for remote code execution, with a particularly obvious payload being the one demonstrated earlier. This vulnerability was initially [reported](https://bugs.chromium.org/p/project-zero/issues/detail?id=2070) by a security researcher from Project Zero.\n\nAlthough the `set-env` command is deprecated and unusable by default, if a developer sets the `ACTIONS_ALLOW_UNSECURE_COMMANDS` environment variable in a workflow, the `set-env` command becomes available and can be used:\n\n![alibaba](img/alibaba.png)\n\n#### examples\n\n- [alibaba/nacos](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)\n\n\n### bot-check\n\nIt's possible to bypass the following check:\n\n```yaml\njobs:\n  merge-dependabot-pr:\n    runs-on: ubuntu-latest\n    if: github.actor == 'dependabot[bot]'\n    steps:\n\t\t...\n```\n\nThe idea of the attack is to trigger Dependabot on a forked repository in such a way that a PR on the forked repository is made by Dependabot, then a PR from the Dependabot branch is opened on the vulnerable repository and finally Dependabot is triggered again to launch the vulnerable workflow.\n\n![dependabot](img/dependabot.png)\n\nYou can find all the exploitation details here: https://www.synacktiv.com/publications/github-actions-exploitation-dependabot\n\n#### examples\n\n- [spring-projects/spring-security](https://www.synacktiv.com/publications/github-actions-exploitation-dependabot)\n- [spring-projects/spring-session](https://www.synacktiv.com/publications/github-actions-exploitation-dependabot)\n- [trpc/trpc](https://www.synacktiv.com/publications/github-actions-exploitation-dependabot)\n\n### known-vulnerability\n\nSearch for known vulnerable actions based on [osv.dev](https://osv.dev/list?ecosystem=GitHub+Actions\u0026q=).\n\n### dangerous-artefact\n\nCheck for workflow that upload artefacts containing sensitive files like `.git/config`. This rule is based on [this article](https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/).\n\n### credentials\n\nChecks for credentials in `services:` configuration. This rules comes from [actionlint](https://github.com/rhysd/actionlint).\n\n![test](img/test.png)\n\n### shellcheck\n\nRun shellcheck on all the bash tasks. This rules comes from [actionlint](https://github.com/rhysd/actionlint).\n\n### local-action\n\nRaise an alert if a local GitHub action is used. For now the tool can't parse local action files so an alert is raised as they can also contain vulnerabilities.\n\n### oidc-action\n\nDetect OIDC actions. Workflows using OIDC actions can be a good target to access some cloud providers. There is no vulnerability associated with this rule but taking a closer look at this action can be interesting if there is a vulnerability that is not found by this tool.\n\n💡 This is now a debug rule, you need to add `--debug-rules` to search for this one.\n## Credits\n\nThis tool could not have been developed without [actionlint](https://github.com/rhysd/actionlint). Many thanks to [@rhysd](https://github.com/rhysd).\n\n## Resources\n\n- [GitHub Actions exploitation: introduction](https://www.synacktiv.com/publications/github-actions-exploitation-introduction)\n- [GitHub Actions exploitation: untrusted input](https://www.synacktiv.com/publications/github-actions-exploitation-untrusted-input)\n- [GitHub Actions exploitation: repo jacking and environment manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)\n- [GitHub Actions exploitation: self hosted runners](https://www.synacktiv.com/publications/github-actions-exploitation-self-hosted-runners)\n- [GitHub Actions exploitation: Dependabot](https://www.synacktiv.com/publications/github-actions-exploitation-dependabot)\n- https://0xn3va.gitbook.io/cheat-sheets/ci-cd/github/actions\n- https://cloud.hacktricks.xyz/pentesting-ci-cd/github-security\n- https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens\n","funding_links":[],"categories":["Static workflow file scanning","Tools"],"sub_categories":["ArgoCD"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsynacktiv%2Foctoscan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsynacktiv%2Foctoscan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsynacktiv%2Foctoscan/lists"}