GHSA-x6gv-2rvh-qmp6 #
Summary #
| Field | Value |
|---|---|
| CVE | N/A |
| Affected Action | BoldestDungeon/steam-workshop-deploy, m00nl1ght-dev/steam-workshop-deploy |
| Severity | Critical (CVSS 10.0) |
| Vulnerability Type | Exposure of Version-Control Repository and Insufficiently Protected Credentials (CWE-212, CWE-522, CWE-527) |
| Published | August 13, 2025 |
Vulnerability Description #
The steam-workshop-deploy GitHub Action (both BoldestDungeon and m00nl1ght-dev variants) fails to exclude .git directories during content packaging, leading to exposure of repository metadata and credentials, including GitHub personal access tokens (PATs).
Root Cause: The action packages content for deployment without filtering .git directories or implementing built-in exclusion mechanisms. When .git folders exist in target directories, they are silently included in the output package, resulting in exposure of repository metadata and potentially long-lived credentials stored in .git/config.
Affected Versions:
- BoldestDungeon/steam-workshop-deploy: v1, v1.0.1
- m00nl1ght-dev/steam-workshop-deploy: v1, v2, v3
Patched Versions:
- BoldestDungeon/steam-workshop-deploy: v2.0.0
- m00nl1ght-dev/steam-workshop-deploy: v4
This vulnerability is a variant of the “Artipacked” attack pattern where:
actions/checkoutstoresGITHUB_TOKENin.git/config(default behavior withpersist-credentials: true)- Deployment action uploads entire workspace including
.gitdirectory .git/configcontains credential helper with valid GitHub token or PAT- Anyone downloading the Workshop item can extract the token
- Token grants access to the repository with original workflow permissions
Severity Assessment (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N):
- Critical (7.0-10.0): Long-lived tokens, organization-wide credentials, or credentials with administrative privileges were exposed
- Medium (4.0-6.9): Credentials with limited repository access and/or short lifespan (e.g., ephemeral tokens) were exposed
- Low (0.0-3.9): Only non-sensitive metadata exposed
Attack Scenarios with Elevated Risk:
- Self-hosted runners storing long-lived tokens
- Developers maintaining
.gitfolders with embedded PATs in.git/config - Workflows running without
actions/checkoutbut with existing.gitdirectories - Use of non-ephemeral tokens passed to
actions/checkout
Security Consequences:
- Unauthorized repository access via exposed PATs
- Repository code or metadata tampering
- Malicious CI behavior triggering through workflow_dispatch
- Disclosure of commit history and internal repository structure
This is particularly critical because:
- Steam Workshop items are often public
.git/configis plaintext and easy to extractGITHUB_TOKENmay have write permissions- Attack can be automated to extract credentials from public Workshop items
- While GitHub hosted runners revoke ephemeral credentials automatically, risk increases significantly with self-hosted runners
Vulnerable Pattern #
name: Vulnerable Pattern
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# Vulnerable: Default checkout persists credentials
# .git/config will contain credential helper with GITHUB_TOKEN
- uses: actions/checkout@v4
# persist-credentials: true (default)
- name: Build mod
run: |
npm install
npm run build
# Vulnerable: Deploys entire workspace including .git
# .git/config with credentials is uploaded to Steam Workshop
- uses: BoldestDungeon/steam-workshop-deploy@v1
with:
username: ${{ secrets.STEAM_USERNAME }}
password: ${{ secrets.STEAM_PASSWORD }}
app_id: '123456'
workshop_id: '789012'
path: . # Uploads everything including .git
Why this is vulnerable:
actions/checkoutdefault:persist-credentials: true.git/configcontains credential helper withGITHUB_TOKEN- Entire workspace (including
.git) uploaded to Steam Workshop - Public Workshop item exposes credentials to anyone
Example .git/config content:
[credential "https://github.com"]
helper = !gh auth git-credential
[credential]
helper = store --file=/home/runner/work/_temp/.git-credentials
The credentials file contains:
https://x-access-token:ghs_xxxxxxxxxxxxxxxxxxxxx@github.com
Safe Pattern #
name: Safe Pattern
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# Safe: Disable credential persistence
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Build mod
run: |
npm install
npm run build
# Safe: Deploy only build output, not source
- uses: BoldestDungeon/steam-workshop-deploy@v1
with:
username: ${{ secrets.STEAM_USERNAME }}
password: ${{ secrets.STEAM_PASSWORD }}
app_id: '123456'
workshop_id: '789012'
path: ./dist # Only deploy build artifacts
Why this is safe:
persist-credentials: falseprevents credential storage in.git- Deploy only
./distdirectory (build output), not source code - No
.gitdirectory in deployed content - Credentials never leave GitHub Actions runner
Alternative Safe Pattern (if source needed):
- name: Remove .git before deploy
run: rm -rf .git
- uses: BoldestDungeon/steam-workshop-deploy@v1
with:
path: .
sisakulint Detection Result #
script/actions/advisory/GHSA-x6gv-2rvh-qmp6-vulnerable.yaml:27:9: Action 'BoldestDungeon/steam-workshop-deploy@v1' has a known critical severity vulnerability (GHSA-x6gv-2rvh-qmp6): m00nl1ght-dev/steam-workshop-deploy: Exposure of Version-Control Repository to an Unauthorized Control Sphere and Insufficiently Protected Credentials. Upgrade to version 2.0.0 or later. See: https://github.com/advisories/GHSA-x6gv-2rvh-qmp6 [known-vulnerable-actions]
Analysis #
| Detected | Rule | Category Match |
|---|---|---|
| Yes | KnownVulnerableActionsRule | Yes |
Detection Details:
KnownVulnerableActionsRuledetects the specific vulnerable action version at line 27 and recommends upgrading to version 2.0.0 or later- The rule correctly identifies this as a critical severity vulnerability related to credential exposure
- Auto-fix available: Suggests upgrading to the patched version
Detection Logic:
- Identifies the use of
BoldestDungeon/steam-workshop-deploy@v1 - Matches against the known vulnerability database (GHSA-x6gv-2rvh-qmp6)
- Reports the specific vulnerability with upgrade recommendation
Mitigation Recommendations #
- Always set
persist-credentials: false: Unless you need Git operations with authentication - Deploy only build artifacts: Use
path: ./distnotpath: . - Audit deployed content: Check if
.gitwas included in past deployments - Rotate GITHUB_TOKEN: Impossible (automatic), but review repository access logs
- Review Workshop items: Check if credentials were exposed in public items
- Add pre-deploy validation: Verify
.gitdirectory is not present - Use deployment protection: Require manual approval for production deploys
- Limit token permissions: Use minimal
permissions:in workflow
Attack Scenario #
Deployment Phase:
- uses: actions/checkout@v4 # persist-credentials: true (default) - run: npm run build - uses: steam-workshop-deploy@v1 with: path: . # Uploads .git directoryAttacker Phase:
# Download Workshop item steamcmd +workshop_download_item 123456 789012 # Extract credentials cd mod_directory cat .git/config # [credential "https://github.com"] # helper = store --file=/tmp/.git-credentials cat /tmp/.git-credentials # https://x-access-token:ghs_xxxxxxxxx@github.com # Use stolen token git clone https://x-access-token:ghs_xxxxxxxxx@github.com/victim/repo.git cd repo git commit --allow-empty -m "backdoor" git push # Success - token has write permissionImpact:
- Attacker gains repository write access
- Can inject malicious code
- Access to repository secrets (in subsequent workflow runs)
- Potential for supply chain attack
Technical Details #
Why credentials are persisted:
GitHub’s actions/checkout uses credential helpers to enable seamless Git operations:
# What checkout does (simplified)
git config credential.helper "store --file=$RUNNER_TEMP/.git-credentials"
echo "https://x-access-token:$GITHUB_TOKEN@github.com" > $RUNNER_TEMP/.git-credentials
Token Permissions:
GITHUB_TOKEN typically has:
contents: write(push commits)metadata: read(read repository metadata)- Can trigger workflows (workflow_dispatch)
Technical Fix Details #
Version 2.0.0 (BoldestDungeon) and v4 (m00nl1ght-dev) implemented:
Built-in
.deployignoremechanism - A new exclusion system similar to.gitignorethat filters sensitive files before packaging:- Git directories and configuration (
.git/,.gitignore,.gitattributes,.github/) - Steam credentials (
config.vdf,localconfig.vdf,DialogConfig.vdf,ssfn*,*.acf) - Common sensitive files (
.env,.env.*,.pem,.key,.crt)
- Git directories and configuration (
New Action Parameters:
deployIgnore: Path to custom exclusion fileuseBuiltinDeployIgnore: Toggle for built-in exclusions (default:true)stagingPath: Temporary directory for filtered contentconcurrentStaging: Support for concurrent deployments using$GITHUB_SHAsubdirectoriesverbosity: Control output level (NORMALorTRACE)
Implementation using rsync:
- Uses
rsyncwith exclude parameters to copy only approved files to staging directory - Staged content is then packaged and uploaded to Steam Workshop
- Prevents accidental inclusion of sensitive files
- Uses
Changed Files:
.deployignore(new file, +46 lines) - Default exclusion rulesDockerfile(+2 lines) - Updated environmentaction.yml(+26/-1 lines) - Added new parameterssteam_deploy.sh(+66/-1 lines) - Implemented rsync-based filtering
References #
- GitHub Advisory: GHSA-x6gv-2rvh-qmp6
- BoldestDungeon Security Advisory
- Real-world Example: GHSA-7j9v-72w9-ww6w - Affected mod example
- Fix Commit (BoldestDungeon)
- Fix Commit (m00nl1ght-dev)
- Release v2.0.0 (BoldestDungeon)
- Release v4 (m00nl1ght-dev)
- sisakulint: ArtipackedRule
- actions/checkout: persist-credentials
- Artipacked Attack Pattern
- GitHub: Security hardening