Catalog PyPI
Back to packageRELEASE DETAILS

1.0.0

PyPI

actions-scanner

GitHub Actions vulnerability scanner for PwnRequest attacks

actionsgithubscannersecurityvulnerability
Published4 Mar 2026
Direct dependencies15
Transitive dependenciesNot calculated

README

Version 1.0.0 documentation
= GitHub Actions PwnRequest Vulnerability Scanner

A toolkit for detecting GitHub Actions workflows vulnerable to the https://securitylab.github.com/research/github-actions-preventing-pwn-requests/[PwnRequest] attack pattern — where `pull_request_target` workflows check out and execute untrusted PR code, allowing secret exfiltration.

CAUTION: Use findings responsibly and follow responsible disclosure practices.

== Installation

[source,bash]
----
uv tool install git+https://github.com/SecKatie/actions-scanner.git
----

== Usage

[source,bash]
----
# Scan an org, repo URL, local directory, or list file (default format: csv)
actions-scanner scan your-org -o results.csv

# Validate findings with AI-assisted confirmation (accepts csv or json)
actions-scanner validate results.csv

# Export to markdown
actions-scanner report results.csv -o report.md --format markdown
----

== What It Detects

The scanner flags workflows where all three conditions exist in the same job:

1. **Trigger:** `pull_request_target` (runs with write permissions and secrets)
2. **Checkout:** Untrusted PR ref (`head.sha`, `head.ref`, `merge_commit_sha`, `github.head_ref`)
3. **Execution:** Build commands (`npm install`, `make`, `pip install`, `docker build`, etc.) or local actions (`./action`)

== Development

**Prerequisites:** Python 3.11+, https://github.com/astral-sh/uv[uv], `gh` CLI (authenticated), `git`

[source,bash]
----
git clone https://github.com/SecKatie/actions-scanner.git && cd actions-scanner
uv sync
uv run pytest tests/ -v
----

=== Environment Variables

[cols="1,2"]
|===
|Variable |Description

|`GITHUB_TOKEN`
|GitHub personal access token (required for org scanning)
|===

== References

* https://securitylab.github.com/research/github-actions-preventing-pwn-requests/[GitHub Security Lab: Preventing pwn requests]
* https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions[GitHub Docs: Security hardening for GitHub Actions]

== License

Internal use only.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 23

Native requirements
15 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
aiofiles
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aiofiles>=23.2.0runtime
aiohttp
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aiohttp>=3.9.0runtime
click
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
click>=8.1.7runtime
pydantic-settings
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pydantic-settings>=2.1.0runtime
pydantic
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pydantic>=2.5.0runtime
pyyaml
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pyyaml>=6.0.1runtime
rich
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
rich>=13.7.0runtime
aioresponses
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aioresponses>=0.7.0; extra == "all"runtime
ldap3
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
ldap3>=2.9.1; extra == "all"runtime
pre-commit
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pre-commit>=3.6.0; extra == "all"runtime
pytest-asyncio
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pytest-asyncio>=0.23.0; extra == "all"runtime
pytest-cov
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pytest-cov>=4.1.0; extra == "all"runtime
pytest
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pytest>=7.4.0; extra == "all"runtime
ruff
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
ruff>=0.4.0; extra == "all"runtime
ty
Environment
{
  "extras": "",
  "marker": "extra == \"all\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
ty>=0.0.1; extra == "all"runtime
aioresponses
Environment
{
  "extras": "",
  "marker": "extra == \"dev\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aioresponses>=0.7.0; extra == "dev"runtime
pre-commit
Environment
{
  "extras": "",
  "marker": "extra == \"dev\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pre-commit>=3.6.0; extra == "dev"runtime
pytest-asyncio
Environment
{
  "extras": "",
  "marker": "extra == \"dev\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pytest-asyncio>=0.23.0; extra == "dev"runtime
pytest-cov
Environment
{
  "extras": "",
  "marker": "extra == \"dev\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pytest-cov>=4.1.0; extra == "dev"runtime
pytest
Environment
{
  "extras": "",
  "marker": "extra == \"dev\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
pytest>=7.4.0; extra == "dev"runtime
ruff
Environment
{
  "extras": "",
  "marker": "extra == \"dev\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
ruff>=0.4.0; extra == "dev"runtime
ty
Environment
{
  "extras": "",
  "marker": "extra == \"dev\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
ty>=0.0.1; extra == "dev"runtime
ldap3
Environment
{
  "extras": "",
  "marker": "extra == \"ldap\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
ldap3>=2.9.1; extra == "ldap"runtime

Distribution files

2 collected
actions_scanner-1.0.0-py3-none-any.whl

bdist_wheel · 68.73 KB · Python py3

Source file
actions_scanner-1.0.0.tar.gz

sdist · 72.41 KB · Python source

Source file

Repository

PENDING
seckatie/actions-scanner

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 18:34 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:6c9ea9bbba3f7fff0c3b402d33baa93a8a5697a0aeded182b21abfdf2e45e9f5

Other collected versions

View all collected versions →