SecScore
ActionsTags
(2)🇺🇸 English | 🇧🇷 Português
Security Score that matters.
Security Scanner
↓
SARIF
↓
SecScore
↓
PASS / REVIEW / FAIL
SecScore is a lightweight security scoring engine for CI/CD pipelines. It evaluates findings from security scanners and calculates a single security score for a Pull Request, allowing teams to automatically decide whether a change should PASS, require REVIEW, or FAIL.
The tool is scanner-agnostic and works with SARIF, making it compatible with most modern security scanners.
Security scanners generate findings. But pipelines need decisions.
Scanner → Findings → SecScore → Score → Decision
Score: 82 / 100
Decision: REVIEW
- Security score for Pull Requests
- Hard fail rules for critical vulnerabilities
- SARIF compatible (Snyk, CodeQL, Semgrep, Checkmarx, etc.)
- Multi-SARIF support — pass multiple scanner outputs in one run
- Diff-aware filtering — evaluates only findings introduced in the PR
- Suppressions by fingerprint — suppress confirmed false positives traceably
- Optional M.A.R.I.A integration — submits SecScore decision payload (
Score,Decision,Summary) after analysis - GitHub Action ready
- Policy-driven security decisions
- Lightweight and fast
- Open source
Security Scanner
↓
SARIF
↓
SecScore Parser
↓
Policy Engine
↓
Score Calculation
↓
PASS / REVIEW / FAIL
Supported scanners:
- Snyk
- Semgrep
- CodeQL
- Checkmarx
- Trivy
- Any SARIF-compatible scanner
| Scanner | Format |
|---|---|
| Snyk | SARIF |
| CodeQL | SARIF |
| Semgrep | SARIF |
| Checkmarx | SARIF |
| Checkmarx API | JSON |
Clone the repository:
git clone https://github.com/cassiodeveloper/secscore
cd secscore
Install dependencies:
pip install -r requirements.txt
- Run with SARIF and policy:
python -m secscore.cli.main pr \
--sarif tests/fixtures/review.sarif \
--policy policy/policy-pr.yml \
--no-diff-aware- Check outputs:
pr-comment.md(PR-ready markdown summary)secscore-result.json(structured result)
- Optional: submit result to M.A.R.I.A:
python -m secscore.cli.main pr \
--sarif tests/fixtures/review.sarif \
--policy policy/policy-pr.yml \
--maria-url http://localhost:5213/api/secscore/submissions \
--maria-repository-id 11111111-2222-3333-4444-555555555555 \
--token YOUR_MARIA_TOKEN \
--no-diff-awareUse these commands to validate expected outcomes quickly:
python -m secscore.cli.main pr \
--sarif tests/fixtures/pass.sarif \
--policy policy/policy-pr.yml \
--no-diff-awareExpected: Decision: PASS
python -m secscore.cli.main pr \
--sarif tests/fixtures/review.sarif \
--policy policy/policy-pr.yml \
--no-diff-awareExpected: Decision: REVIEW
python -m secscore.cli.main pr \
--sarif tests/fixtures/fail.sarif \
--policy policy/policy-pr.yml \
--no-diff-awareExpected: Decision: FAIL
| Mode | When to use | Required flags |
|---|---|---|
SARIF (--sarif) |
You already generated scanner SARIF files in CI | --sarif, --policy |
Findings JSON (--findings) |
You already have normalized findings JSON | --findings, --policy |
Provider (--provider checkmarx) |
You want SecScore to fetch findings directly from provider API | --provider checkmarx, provider flags, --policy |
Single SARIF file:
python -m secscore.cli.main pr \
--sarif examples/example-snyk.sarif \
--policy policy/policy-pr.yml \
--no-diff-aware
Multiple SARIF files (v0.3.0+):
python -m secscore.cli.main pr \
--sarif semgrep.sarif,trivy.sarif \
--policy policy/policy-pr.yml \
--no-diff-aware
Send consolidated findings to M.A.R.I.A (token provided at invocation):
python -m secscore.cli.main pr \
--sarif semgrep.sarif,trivy.sarif \
--policy policy/policy-pr.yml \
--maria-url https://demo.mariaappsec.com/api/secscore/submissions \
--maria-repository-id 11111111-2222-3333-4444-555555555555 \
--token YOUR_MARIA_TOKEN \
--no-diff-awareFor /api/secscore/submissions, SecScore auto-fills required submission fields
(Score, Decision, Summary, CommitSha, BranchName, PipelineName, PipelineRunId, SubmissionKey).
You can override them with:
--maria-submission-key, --maria-commit-sha, --maria-branch-name, --maria-pipeline-name, --maria-pipeline-run-id, --maria-pull-request-id.
For local PR testing without opening a real PR:
SECSCORE_PULL_REQUEST_ID=local-pr-001 python -m secscore.cli.main pr \
--sarif semgrep.sarif \
--policy policy/policy-pr.yml \
--maria-url http://localhost:5213/api/secscore/submissions \
--maria-repository-id 11111111-2222-3333-4444-555555555555 \
--token YOUR_MARIA_TOKEN \
--no-diff-awareIn GitHub Actions, SecScore auto-detects the pull request number from the
pull_request event. Other CI variables supported: CI_MERGE_REQUEST_IID,
CI_MERGE_REQUEST_ID, SYSTEM_PULLREQUEST_PULLREQUESTID, and BITBUCKET_PR_ID.
Note: use
--no-diff-awarewhen running locally without a full git history. In CI, diff-aware is enabled by default and requiresfetch-depth: 0in the checkout step.
Example output:
Score: 85 / 100
Decision: PASS
Minimal example:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run SecScore
uses: cassiodeveloper/secscore@v1
with:
sarif: results.sarifMultiple scanners (v0.3.0+):
- name: Run SecScore
uses: cassiodeveloper/secscore@v1
with:
sarif: "semgrep.sarif,trivy.sarif"
maria-url: "https://demo.mariaappsec.com/api/secscore/submissions"
maria-repository-id: "11111111-2222-3333-4444-555555555555"
maria-token: ${{ secrets.MARIA_TOKEN }}Disable diff-aware:
- name: Run SecScore
uses: cassiodeveloper/secscore@v1
with:
sarif: results.sarif
no_diff_aware: "true"base_score: 100
penalties:
critical: 40
high: 20
medium: 7
low: 2
hard_fails:
- id: SAST_CRITICAL_HIGH_CONF
when:
domain: sast
severity_in: ["critical", "high"]
is_new: true
reason: "New critical/high SAST finding"scoring:
base_score: 100
penalties:
critical: 40
high: 20
medium: 7
low: 2
multipliers:
confidence:
high: 1.0
medium: 0.8
low: 0.5
decision:
pass_min_score: 85
review_min_score: 51
hard_fails:
- id: CRITICAL_NEW
when:
severity_in: ["critical"]
is_new: true
reason: "New critical finding"
ignore_paths:
- "node_modules/**"
- "dist/**"suppressions:
deny_fingerprints:
- "abc123def456" # confirmed false positive — XSS in test helperObtain the fingerprint from secscore-result.json > hard_fails[].finding_fingerprint.
404 Not Foundon M.A.R.I.A: endpoint path is wrong; use/api/secscore/submissions.400 Bad Requeston M.A.R.I.A: payload contract mismatch (required submission fields missing/invalid).401 Unauthorizedon M.A.R.I.A: invalid token format/value for that environment.403 Forbiddenon M.A.R.I.A: token valid but missing scope/resource access to the target repository.Diff-aware skippedwarning: expected locally without full git history; use--no-diff-aware.
Example SARIF files:
examples/
example-snyk.sarif
example-checkmarx.sarif
Example workflows:
examples/workflows/
example-minimal.yml
example-snyk.yml
example-checkmarx.yml
example-checkmarx-api.yml
example-multi-scanner.yml
secscore/
adapters/
cli/
core/
normalizers/
utils/
examples/
policy/
schema/
If you discover a vulnerability in this project, please report it responsibly.
Contributions are welcome. Please read:
This project is licensed under the PolyForm Noncommercial License 1.0.0.
Free for non-commercial use. Commercial use — including incorporation into a paid product, service, or platform — requires explicit permission from the author.
LICENSE · polyformproject.org/licenses/noncommercial/1.0.0
Security scanners generate noise.
SecScore focuses on what actually matters:
clear, automated security decisions in CI/CD pipelines.
Resources
SecScore is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.