Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
41 changes: 41 additions & 0 deletions .github/templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# PR Body Templates

This directory contains templates for onboarding PRs that add the Security Code Scanner to repositories.

## Templates

### `onboarding-pr-body-manual.md`

**Use for:** Manual PRs created by the security team

- More detailed with full language configuration examples
- Includes code snippets for common scenarios
- Comprehensive documentation
- No auto-merge disclaimer

### `onboarding-pr-body-automated.md`

**Use for:** Automated PRs created by workflows

- Shorter, more concise
- Includes auto-merge warning at the top
- Links to README for detailed configuration
- Used by `.github/workflows/onboard-new-repo.yml`

## Variables

Both templates support variable substitution:

- `{{SECURITY_SCANNING_URL}}` - Repository-specific code scanning alerts URL

## Usage

**Manual PRs:**

```bash
# Copy and paste from onboarding-pr-body-manual.md
# Replace {{SECURITY_SCANNING_URL}} with actual URL
```

**Automated workflow:**
The workflow automatically reads `onboarding-pr-body-automated.md` and substitutes variables.
98 changes: 98 additions & 0 deletions .github/templates/onboarding-pr-body-automated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
## ⚠️ Important Notice - Action Required

**This PR may be auto-merged in the future if not configured.**

If your team does not need the security scanner:

1. **Add a comment on this PR** explaining why your team is opting out
2. **Close this PR** to prevent auto-merge
3. **Add a `.github/no-security-scanner` file** to your repository to prevent future onboarding attempts
Comment thread
cursor[bot] marked this conversation as resolved.

If you need the scanner but want to customize it:

1. Complete the checklist below
2. Review and modify the workflow file as needed
3. Approve and merge this PR when ready

If no action is taken, this PR may be automatically merged after a grace period to ensure baseline security coverage across all repositories.

---

## Required Action

Prior to merging this pull request, please ensure the following has been completed:

- [ ] The lines specifying `branches` correctly specify this repository's default branch (usually `main` or `master`).
- [ ] Any paths you would like to ignore have been added to the `paths-ignored` configuration option (see [setup](https://github.com/MetaMask/action-security-code-scanner/blob/main/README.md#setup))
- [ ] Language configuration has been reviewed - ignore falsely detected languages or add build commands for Java/Kotlin if needed (see Configuration section below)
- [ ] Any existing CodeQL configuration has been disabled.

## What is the Security Code Scanner?

This pull request enables the [MetaMask Security Code Scanner](https://github.com/MetaMask/action-security-code-scanner) GitHub Action. This action runs on each pull request, and will flag potential vulnerabilities as a review comment. It will also scan this repository's default branch, and log any findings in this repository's [Code Scanning Alerts Tab]({{SECURITY_SCANNING_URL}}).

<img width="500" alt="Security Scanner Screenshot" src="https://github.com/user-attachments/assets/41c87b70-79b7-44dd-a444-791b142fbbe1">

The action itself runs various static analysis engines behind the scenes. Currently, it is only running GitHub's CodeQL engine. For this reason, we recommend disabling any existing CodeQL configuration your repository may have.

## How do I interact with the tool?

Every finding raised by the Security Code Scanner will present context behind the potential vulnerability identified, and allow the developer to fix, or dismiss it.

The finding will automatically be dismissed by pushing a commit that fixes the identified issue, or by manually dismissing the alert using the button in GitHub's UI. If dismissing an alert manually, please add any additional context surrounding the reason for dismissal, as this informs our decision to disable, or improve any poor performing rules.

<img width="983" alt="Alert Dismissal Screenshot" src="https://github.com/user-attachments/assets/114219d5-4b4c-4d9d-8bfe-f4666012b73e">

## Configuration

### Language Configuration

The scanner auto-detects languages in your repository. If you need to customize language-specific settings, you can modify the `languages-config` section in the workflow file.

**Common use cases:**

1. **Ignore falsely detected languages:**

```yaml
languages-config: |
[
{
"language": "ruby",
"ignore": true
}
]
```

2. **Configure Java/Kotlin builds:**

```yaml
languages-config: |
[
{
"language": "java-kotlin",
"build_mode": "manual",
"build_command": "./gradlew build",
"version": "21",
"distribution": "temurin"
}
]
```

**Supported languages:** `javascript-typescript`, `python`, `java-kotlin`, `go`, `cpp`, `csharp`, `ruby`

**Build modes:** `none`, `autobuild`, `manual`

### Additional Configuration

For more configuration options, please review the tool's [README](https://github.com/MetaMask/action-security-code-scanner/blob/main/README.md).

Optional secrets that can be configured:

- `SECURITY_SCAN_METRICS_TOKEN` - for metrics collection
- `APPSEC_BOT_SLACK_WEBHOOK` - for Slack notifications

For any additional questions, please reach out to `@app-sec` in Slack.
Comment thread
witmicko marked this conversation as resolved.
Outdated

---

🤖 _This PR was automatically created by the MetaMask Security onboarding system_
51 changes: 51 additions & 0 deletions .github/templates/security-code-scanner.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: MetaMask Security Code Scanner

on:
push:
branches:
- { DEFAULT_BRANCH }
pull_request:
branches:
- { DEFAULT_BRANCH }
workflow_call:
secrets:
SECURITY_SCAN_METRICS_TOKEN:
required: false
APPSEC_BOT_SLACK_WEBHOOK:
required: false
workflow_dispatch:

jobs:
security-scan:
uses: MetaMask/action-security-code-scanner/.github/workflows/security-scan.yml@v2
permissions:
actions: read
contents: read
security-events: write
with:
repo: ${{ github.repository }}
scanner-ref: 'v2'
paths-ignored: |
node_modules
**/node_modules/**
**/__snapshots__/**
__snapshots_linux__
**/__stories__/**
.storybook/
**/*.test.ts
**/*.test.tsx
**/*.test.js
**/*.test.jsx
**/*.spec.ts
**/*.spec.tsx
**/*.spec.js
**/*.spec.jsx
**/test*/**
**/e2e/**
**/tests/**
languages-config: |
[
]
secrets:
project-metrics-token: ${{ secrets.SECURITY_SCAN_METRICS_TOKEN }}
slack-webhook: ${{ secrets.APPSEC_BOT_SLACK_WEBHOOK }}
180 changes: 180 additions & 0 deletions .github/workflows/onboard-new-repo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
name: 'Onboard New Repository with SAST'

on:
workflow_dispatch:
inputs:
organization:
description: 'Organization name (e.g., MetaMask)'
required: true
type: string
repository:
description: 'Repository name (e.g., snaps)'
required: true
type: string
repository_dispatch:
types: [new_repository_created]

jobs:
create-sast-pr:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout scanner action repository
uses: actions/checkout@v4
with:
path: scanner-repo

- name: Determine target repository
id: target
run: |
if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
ORG="${{ github.event.client_payload.organization }}"
REPO_NAME="${{ github.event.client_payload.repository }}"
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
REPO="$ORG/$REPO_NAME"
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
else
REPO="${{ inputs.organization }}/${{ inputs.repository }}"
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
fi

# Auto-detect default branch from the repository
echo "Detecting default branch for $REPO..."
BASE_BRANCH=$(gh api "repos/$REPO" --jq '.default_branch' 2>/dev/null || echo "")

# If repo is empty or API call failed, default to 'main'
if [ -z "$BASE_BRANCH" ] || [ "$BASE_BRANCH" = "null" ]; then
echo "Repository is empty or default branch not found. Defaulting to 'main'"
BASE_BRANCH="main"
fi

echo "repository=$REPO" >> "$GITHUB_OUTPUT"
echo "base_branch=$BASE_BRANCH" >> "$GITHUB_OUTPUT"
Comment thread
cursor[bot] marked this conversation as resolved.
shell: bash
env:
GH_TOKEN: ${{ secrets.ONBOARDING_TOKEN }}

- name: Check if target repository is empty
id: check_empty
run: |
REPO="${{ steps.target.outputs.repository }}"
# Try to get repository info
REPO_INFO=$(gh api "repos/$REPO" 2>/dev/null || echo "")

if [ -z "$REPO_INFO" ]; then
echo "Failed to get repository info"
exit 1
fi

# Check if repository has commits (size will be 0 if empty)
IS_EMPTY=$(echo "$REPO_INFO" | jq -r '.size == 0')

echo "is_empty=$IS_EMPTY" >> "$GITHUB_OUTPUT"
Comment thread
witmicko marked this conversation as resolved.
echo "Repository empty status: $IS_EMPTY"
shell: bash
env:
GH_TOKEN: ${{ secrets.ONBOARDING_TOKEN }}
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
witmicko marked this conversation as resolved.

- name: Checkout target repository
if: steps.check_empty.outputs.is_empty == 'false'
uses: actions/checkout@v4
with:
repository: ${{ steps.target.outputs.repository }}
token: ${{ secrets.ONBOARDING_TOKEN }}
path: target-repo
ref: ${{ steps.target.outputs.base_branch }}
Comment thread
cursor[bot] marked this conversation as resolved.

- name: Initialize empty repository locally
if: steps.check_empty.outputs.is_empty == 'true'
run: |
mkdir -p target-repo
cd target-repo
git init
git remote add origin "https://x-access-token:${{ secrets.ONBOARDING_TOKEN }}@github.com/${{ steps.target.outputs.repository }}.git"
shell: bash

- name: Create branch and add SAST workflow
working-directory: target-repo
run: |
git config user.name "MetaMask Security Bot"
git config user.email "security-bot@metamask.io"

IS_EMPTY="${{ steps.check_empty.outputs.is_empty }}"
BASE_BRANCH="${{ steps.target.outputs.base_branch }}"

if [ "$IS_EMPTY" = "true" ]; then
# For empty repos, create initial commit on main
BRANCH_NAME="$BASE_BRANCH"
else
# For existing repos, create a feature branch
BRANCH_NAME="security/add-sast-scanner"
git checkout -b "$BRANCH_NAME"
fi
Comment thread
cursor[bot] marked this conversation as resolved.

# Create .github/workflows directory if it doesn't exist
mkdir -p .github/workflows

# Copy the security scanner workflow template and replace placeholders
sed "s/{ DEFAULT_BRANCH }/$BASE_BRANCH/g" \
../scanner-repo/.github/templates/security-code-scanner.yml \
> .github/workflows/security-code-scanner.yml
Comment thread
witmicko marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.

git add .github/workflows/security-code-scanner.yml
git commit -m "chore: add MetaMask Security Code Scanner workflow

This PR adds the MetaMask Security Code Scanner workflow to enable
automated security scanning of the codebase.

The scanner will run on:
- Push to $BASE_BRANCH branch
- Pull requests to $BASE_BRANCH branch
- Manual workflow dispatch

To configure the scanner for your repository's specific needs,
please review the workflow file and adjust as necessary."

git push -u origin "$BRANCH_NAME"
shell: bash

- name: Create Pull Request
if: steps.check_empty.outputs.is_empty == 'false'
working-directory: target-repo
env:
GH_TOKEN: ${{ secrets.ONBOARDING_TOKEN }}
REPO_NAME: ${{ steps.target.outputs.repository }}
run: |
# Extract owner and repo name for URL construction
OWNER=$(echo "$REPO_NAME" | cut -d'/' -f1)
REPO=$(echo "$REPO_NAME" | cut -d'/' -f2)
BASE_BRANCH="${{ steps.target.outputs.base_branch }}"
SECURITY_URL="https://github.com/${OWNER}/${REPO}/security/code-scanning"

# Read PR body template and substitute variables
PR_BODY=$(cat ../scanner-repo/.github/templates/onboarding-pr-body-automated.md)
PR_BODY="${PR_BODY//\{\{SECURITY_SCANNING_URL\}\}/$SECURITY_URL}"

gh pr create \
--title "🔒 Add MetaMask Security Code Scanner" \
--body "$PR_BODY" \
--base "$BASE_BRANCH" \
--head "security/add-sast-scanner"
shell: bash

- name: Output PR URL
if: steps.check_empty.outputs.is_empty == 'false'
working-directory: target-repo
env:
GH_TOKEN: ${{ secrets.ONBOARDING_TOKEN }}
run: |
PR_URL=$(gh pr view security/add-sast-scanner --json url -q .url)
echo "✅ Pull Request created: $PR_URL"
echo "PR_URL=$PR_URL" >> "$GITHUB_OUTPUT"
shell: bash

- name: Output commit info for empty repo
if: steps.check_empty.outputs.is_empty == 'true'
run: |
REPO="${{ steps.target.outputs.repository }}"
BASE_BRANCH="${{ steps.target.outputs.base_branch }}"
echo "✅ Initial commit pushed to https://github.com/$REPO/tree/$BASE_BRANCH"
echo "Repository was empty - workflow file added directly to $BASE_BRANCH branch"
shell: bash
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Updated language detector to add Github Actions support by default

### Fixed

- Fix incorrect language selection based on repo config ([#63](https://github.com/MetaMask/action-security-code-scanner/pull/63))

## [2.0.1]

### Fixed
Expand Down
Loading
Loading