Skip to content

Commit 507f179

Browse files
committed
add semgrep action
1 parent 379484f commit 507f179

File tree

12 files changed

+417
-0
lines changed

12 files changed

+417
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: 'Reusable Semgrep Analysis'
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
repo:
7+
description: 'Repository that requested the scan'
8+
required: true
9+
type: string
10+
paths_ignored:
11+
description: 'Comma delimited paths to ignore during scan'
12+
required: false
13+
type: string
14+
default: ''
15+
outputs:
16+
sarif-id:
17+
description: 'SARIF file identifier'
18+
value: ${{ jobs.semgrep-analysis.outputs.sarif-id }}
19+
20+
jobs:
21+
semgrep-analysis:
22+
runs-on: ubuntu-latest
23+
permissions:
24+
actions: read
25+
contents: read
26+
security-events: write
27+
28+
outputs:
29+
sarif-id: ${{ steps.upload.outputs.sarif-id }}
30+
31+
steps:
32+
- name: Checkout repository
33+
uses: actions/checkout@v4
34+
with:
35+
repository: ${{ inputs.repo }}
36+
path: ${{ inputs.repo }}
37+
38+
- name: Call Security Code Scanner Semgrep Action
39+
id: semgrep-scan
40+
uses: ./packages/semgrep-action
41+
with:
42+
paths_ignored: ${{ inputs.paths_ignored }}
43+
44+
- name: Upload SARIF
45+
id: upload
46+
uses: github/codeql-action/upload-sarif@v3
47+
with:
48+
sarif_file: semgrep-results.sarif
49+
category: semgrep
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @witmicko
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Contributing to Security Code Scanner Semgrep Rules
2+
3+
This guide is intended to help guide you though the process of adding a new Semgrep rule that this action will run.
4+
5+
## Prerequisites
6+
7+
Before contributing, ensure that you have [Semgrep](https://semgrep.dev/) installed. You can find installation instructions [here](https://semgrep.dev/docs/getting-started/quickstart).
8+
9+
## Writing Your Own Rules
10+
11+
When adding a new rule follow these guidelines:
12+
13+
1. **Start with a Template**
14+
- Use the `rule.template.yml` file located in the root of this repository as a starting point. It includes standard fields and formatting we expect in all rules.
15+
- To create a new rule, copy the template and place it in the appropriate folder:
16+
```bash
17+
cp rule.template.yml rules/src/ < language > / < your-rule-name > .yml
18+
```
19+
20+
2. **Organize Rule Files**
21+
- Rules should be placed in the `rules/src` directory, organized by language or context. For example:
22+
```
23+
rules/src/js/your-js-rule.yml
24+
```
25+
- The rule name (defined in the `id` field) must match the filename, excluding the `.yml` extension. For example:
26+
- Rule filename: `your-js-rule.yml`
27+
- Rule ID: `your-js-rule`
28+
29+
3. **Write and Iterate on the Rule**
30+
- Use the [Semgrep Playground](https://semgrep.dev/playground/new) to draft and refine your rule. If you've never written a Semgrep rule before, I recommend working through [Semgrep's interactive tutorial](https://semgrep.dev/learn). If you want to jump right in, check out Semgrep's [rule writing docs](https://semgrep.dev/docs/writing-rules/overview).
31+
32+
4. **Add Test Files**
33+
- Write tests for your rule in the `rules/test` directory, mirroring the structure of `rules/src`. For example:
34+
```
35+
rules/test/js/your-js-rule.js
36+
```
37+
- Test filenames should match the rule ID and filename, but with the correct file extension (e.g., `.js` for JavaScript tests). Example:
38+
- Rule filename: `your-js-rule.yml`
39+
- Test file: `your-js-rule.js`.
40+
41+
## Rule Metadata
42+
43+
In order for our rules to be rendered correctly within GitHub we require additional metadata properties. The screenshots below illustrate how these properties are displayed within GitHub's inline rule warnings and on the detailed rule information page.
44+
45+
**Example Rule: hello-world**
46+
47+
```
48+
rules:
49+
- id: hello-world
50+
languages:
51+
- js
52+
severity: INFO
53+
metadata:
54+
tags: [security]
55+
shortDescription: Hello, World!
56+
help: "The Security Code Scanner is here to help review your code for vulnerabilities."
57+
confidence: HIGH
58+
message: We saw you said hello world and wanted to say hi back!
59+
pattern: |
60+
console.log("Hello, World!");
61+
```
62+
63+
**Metadata shown on the code scanning alert details page**
64+
<img width="600" alt="" src="https://github.com/user-attachments/assets/d3b79ddf-ab79-46ef-83d7-035fe41fa2fb" />
65+
66+
**Metadata shown on an inline code scanning alert**
67+
<img width="600" alt="" src="https://github.com/user-attachments/assets/e918311f-94d8-4be0-86d8-cc6c30853740" />
68+
69+
## Writing Tests For Your Rules
70+
71+
Testing is a critical step in ensuring the quality and reliability of your rules. Follow these steps:
72+
73+
1. **Write Thorough Test Cases**
74+
- Test files can include:
75+
- **Positive cases**: Examples where the rule should trigger.
76+
- **Negative cases**: Examples where the rule should not trigger.
77+
- Refer to the [Semgrep documentation on testing rules](https://semgrep.dev/docs/writing-rules/testing-rules) for more detailed guidance.
78+
79+
2. **Validate Rule Syntax**
80+
- Use the `bin/validate-rules` script to check for syntax errors:
81+
```bash
82+
./bin/validate-rules
83+
```
84+
85+
3. **Run Tests**
86+
- Use the `bin/test` script to verify that your rule behaves as expected:
87+
```bash
88+
./bin/test
89+
```
90+
91+
## Testing Rules Against Local Repositories
92+
93+
If you would like to test your rules against a local folder or directory on your machine, you can run the following command to perform a local scan:
94+
95+
```bash
96+
./bin/scan path/to/directory
97+
```
98+
99+
Note that Semgrep will scan _all_ files within the specified directory. In other words, if the directory contains multiple repositories, all of them will be scanned at once.
100+
101+
## Contribution Workflow
102+
103+
1. Create a new branch from the main branch for your changes.
104+
2. Add or update rule files and test cases as outlined above.
105+
3. Run the `bin/validate-rules` and `bin/test` scripts to ensure your changes are valid.
106+
4. Open a pull request with a clear explanation of the rule's purpose.
107+
108+
## Notes
109+
110+
- Always consider the specific needs and contexts of your codebase when writing rules. Rules should help identify issues or enforce patterns relevant to security.
111+
- If you encounter challenges or have questions, please reach out to @witmicko.

packages/semgrep-action/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Security Code Scanner - Semgrep Action
2+
3+
This repository is home to the GitHub action workflow that will run perform a semgrep scan on a checked out repository. After the scan is complete, the results will be uploaded to GitHub's Code Scanning API.
4+
5+
## Usage
6+
7+
```
8+
- name: Semgrep Scan
9+
uses: metamask/security-codescanner-monorepo/packages/semgrep-action@main
10+
with:
11+
# optional string parameter
12+
paths_ignored: ...
13+
```
14+
15+
For information on how to contribute rules to this repository, please see the CONTRIBUTING.md file in this package.

packages/semgrep-action/action.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Security Code Scanner - Semgrep
2+
description: 'Runs a Semgrep scan on a repository and uploads the results to GitHub'
3+
4+
inputs:
5+
paths_ignored:
6+
description: 'List of paths or patterns to ignore'
7+
required: false
8+
default: ''
9+
10+
runs:
11+
using: 'composite'
12+
steps:
13+
- name: Copy Semgrep Action Files to Workspace Root
14+
run: |
15+
echo "Copying Semgrep action files to workspace root for flat structure..."
16+
cp -r ${{ github.action_path }}/rules ${{ github.workspace }}/
17+
echo "Files copied. Semgrep rules structure:"
18+
ls -la ${{ github.workspace }}/rules/
19+
shell: bash
20+
21+
- name: Install Semgrep
22+
run: |
23+
pip install semgrep
24+
shell: bash
25+
26+
- name: Generate .semgrepignore
27+
run: |
28+
echo "${{ inputs.paths_ignored }}" > .semgrepignore
29+
cat .semgrepignore
30+
shell: bash
31+
32+
- name: Run Semgrep Scan
33+
run: |
34+
semgrep --config ./rules/src --output semgrep-results.sarif --sarif --verbose
35+
shell: bash
36+
continue-on-error: true
37+
38+
- name: Upload Semgrep Results to GitHub
39+
uses: github/codeql-action/upload-sarif@v3
40+
with:
41+
sarif_file: semgrep-results.sarif
42+
checkout_path: ${{ github.workspace }}

packages/semgrep-action/bin/scan

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
# Run semgrep locally against a directory
3+
4+
if [ -z "$1" ]; then
5+
echo "Usage: $0 <path/to/directory>"
6+
exit 1
7+
fi
8+
9+
# Run semgrep locally against the provided directory
10+
semgrep --config rules/src/ "$1"

packages/semgrep-action/bin/test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
# Run semgrep test cases for specific rules
4+
semgrep --test --config rules/src/ rules/test/
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
# Validate config is valid before testing
4+
semgrep --validate --config ./rules/src
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@metamask/semgrep-action",
3+
"version": "1.0.0",
4+
"private": true,
5+
"description": "Semgrep-based security scanning action",
6+
"keywords": [
7+
"security",
8+
"semgrep",
9+
"github-action"
10+
],
11+
"repository": {
12+
"type": "git",
13+
"url": "git@github.com:MetaMask/security-codescanner-monorepo.git"
14+
},
15+
"license": "ISC",
16+
"author": "witmicko",
17+
"scripts": {
18+
"lint": "prettier '**/*.json' '**/*.md' '**/*.yml' --check --ignore-path .gitignore --no-error-on-unmatched-pattern",
19+
"lint:fix": "prettier '**/*.json' '**/*.md' '**/*.yml' --write --ignore-path .gitignore --no-error-on-unmatched-pattern",
20+
"test": "echo \"Error: no test specified\" && exit 1"
21+
}
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# This template contains the minimum required fields for our Semgrep rules.
2+
# For detailed guidance on additional fields and best practices, refer to: https://semgrep.dev/docs/writing-rules/overview
3+
4+
rules:
5+
- id: # Rule identifier - should match the file name (excluding the extension) for consistency and easy traceability.
6+
languages:
7+
- "..." # See https://semgrep.dev/docs/supported-languages#language-support for supported languages
8+
severity: "..." # Options are INFO, WARN, ERROR. See See https://semgrep.dev/docs/writing-rules/rule-syntax#required
9+
metadata:
10+
tags: [security]
11+
shortDescription: "..." # In GitHub's UI, this is the title of the rule. Keep it concise but descriptive.
12+
help: "..." # Used to provide help documentation, or other resources to help users understand and resolve the finding.
13+
confidence:
14+
"..." # Confidence level of the rule match.
15+
# - LOW: Matches are likely false positives or require significant manual review.
16+
# - MEDIUM: Matches are often correct but may occasionally require review.
17+
# - HIGH: Matches are almost always accurate.
18+
message: "..." # Message shown to the user underneath the `shortDescription` once a rule is triggered.
19+
pattern:
20+
"..." # Add rule pattern here

0 commit comments

Comments
 (0)