feat: add merge-sarif #37
@@ -82,3 +82,25 @@ jobs:
|
||||
|
||||
echo ""
|
||||
echo "All checks passed."
|
||||
|
||||
test-merge-sarif-files:
|
||||
name: Test Merge SARIF Files
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- name: Merge SARIF Files
|
||||
uses: ./merge-sarif-files
|
||||
with:
|
||||
files: |
|
||||
- merge-sarif-files/test_sarif1.json
|
||||
- merge-sarif-files/test_sarif2.json
|
||||
output-file: merged.sarif
|
||||
- name: Check merged SARIF file
|
||||
run: |
|
||||
runs=$(jq -r '.runs' merged.sarif)
|
||||
echo "Runs:"
|
||||
echo "$runs"
|
||||
# check that there are two runs in the array
|
||||
[ "$(echo "$runs" | jq 'length')" -eq 2 ] && echo "OK" || { echo "FAIL (got $(echo "$runs" | jq 'length'), expected 2)"; exit 1; }
|
||||
echo "All checks passed."
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# Merge SARIF Files Action
|
||||
|
||||
A reusable Gitea Action that merges multiple SARIF files into a single SARIF file.
|
||||
|
||||
**Note:** This action only merges the files. It doesn't perform any validation, filtering, or deduplication.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```yaml
|
||||
- name: Merge SARIF Files
|
||||
uses: https://gitea.t000-n.de/t.behrendt/trivy-actions/merge-sarif-files@0.0.1
|
||||
with:
|
||||
files: |
|
||||
- test_sarif1.json
|
||||
- test_sarif2.json
|
||||
output-file: merged.sarif
|
||||
```
|
||||
|
||||
### Complete Example
|
||||
|
||||
```yaml
|
||||
name: Merge SARIF Files
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
merge-sarif-files:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Merge SARIF Files
|
||||
uses: https://gitea.t000-n.de/t.behrendt/trivy-actions/merge-sarif-files@0.0.1
|
||||
with:
|
||||
files: |
|
||||
- test_sarif1.json
|
||||
- test_sarif2.json
|
||||
output-file: merged.sarif
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
| Input | Description | Required | Default |
|
||||
| ------------- | ----------------------------- | -------- | -------------- |
|
||||
| `files` | Paths to SARIF files to merge | Yes | |
|
||||
| `output-file` | Output file path | Yes | `merged.sarif` |
|
||||
@@ -0,0 +1,37 @@
|
||||
name: "Merge SARIF Files"
|
||||
description: "Merge multiple SARIF files into a single SARIF file"
|
||||
author: "Timo Behrendt <t.behrendt@t00n.de>"
|
||||
branding:
|
||||
icon: "database"
|
||||
color: "blue"
|
||||
|
||||
inputs:
|
||||
files:
|
||||
description: "Paths to SARIF files to merge"
|
||||
required: true
|
||||
output-file:
|
||||
description: "Output file path"
|
||||
required: true
|
||||
default: "merged.sarif"
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- id: merge
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
files="${{ inputs.files }}"
|
||||
output-file="${{ inputs.output-file }}"
|
||||
|
||||
# Sarif files are easy to merge. They contain a "runs" array. We just need to concat the runs arrays and write the result to the output file.
|
||||
# Collect all the runs from all
|
||||
runs=()
|
||||
for file in $files; do
|
||||
runs+=($(jq -r '.runs' $file))
|
||||
done
|
||||
|
||||
# Write the merged runs to the output file.
|
||||
echo '{"version":"2.1.0","$schema":"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json","runs":[' > $output-file
|
||||
echo "${runs[@]}" | jq -s '.' >> $output-file
|
||||
echo ']}' >> $output-file
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"fullName": "Trivy Vulnerability Scanner",
|
||||
"informationUri": "https://github.com/aquasecurity/trivy",
|
||||
"name": "Trivy",
|
||||
"rules": [
|
||||
{
|
||||
"id": "KSV-0014",
|
||||
"name": "Misconfiguration",
|
||||
"shortDescription": {
|
||||
"text": "Root file system is not read-only"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "An immutable root file system prevents applications from writing to their local disk. This can limit intrusions, as attackers will not be able to tamper with the file system or write foreign executables to disk."
|
||||
},
|
||||
"defaultConfiguration": {
|
||||
"level": "error"
|
||||
},
|
||||
"helpUri": "https://avd.aquasec.com/misconfig/ksv-0014",
|
||||
"help": {
|
||||
"text": "Misconfiguration KSV-0014\nType: Kubernetes Security Check\nSeverity: HIGH\nCheck: Root file system is not read-only\nMessage: Container 'renovate' of CronJob 'renovate' should set 'securityContext.readOnlyRootFilesystem' to true\nLink: [KSV-0014](https://avd.aquasec.com/misconfig/ksv-0014)\nAn immutable root file system prevents applications from writing to their local disk. This can limit intrusions, as attackers will not be able to tamper with the file system or write foreign executables to disk.",
|
||||
"markdown": "**Misconfiguration KSV-0014**\n| Type | Severity | Check | Message | Link |\n| --- | --- | --- | --- | --- |\n|Kubernetes Security Check|HIGH|Root file system is not read-only|Container 'renovate' of CronJob 'renovate' should set 'securityContext.readOnlyRootFilesystem' to true|[KSV-0014](https://avd.aquasec.com/misconfig/ksv-0014)|\n\nAn immutable root file system prevents applications from writing to their local disk. This can limit intrusions, as attackers will not be able to tamper with the file system or write foreign executables to disk."
|
||||
},
|
||||
"properties": {
|
||||
"precision": "very-high",
|
||||
"security-severity": "8.0",
|
||||
"tags": ["misconfiguration", "security", "HIGH"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "0.69.1"
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "KSV-0014",
|
||||
"ruleIndex": 0,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "Artifact: k8s/31_deployment_redis.yaml\nType: kubernetes\nVulnerability KSV-0014\nSeverity: HIGH\nMessage: Container 'redis' of Deployment 'redis' should set 'securityContext.readOnlyRootFilesystem' to true\nLink: [KSV-0014](https://avd.aquasec.com/misconfig/ksv-0014)"
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "k8s/31_deployment_redis.yaml",
|
||||
"uriBaseId": "ROOTPATH"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 34,
|
||||
"startColumn": 1,
|
||||
"endLine": 51,
|
||||
"endColumn": 1
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"text": "k8s/31_deployment_redis.yaml"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"fullName": "Trivy Vulnerability Scanner",
|
||||
"informationUri": "https://github.com/aquasecurity/trivy",
|
||||
"name": "Trivy",
|
||||
"rules": [
|
||||
{
|
||||
"id": "CVE-2026-26019",
|
||||
"name": "LanguageSpecificPackageVulnerability",
|
||||
"shortDescription": {
|
||||
"text": "@langchain/community: @langchain/community SSRF Bypass in RecursiveUrlLoader"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "LangChain is a framework for building LLM-powered applications. Prior to 1.1.14, the RecursiveUrlLoader class in @langchain/community is a web crawler that recursively follows links from a starting URL. Its preventOutside option (enabled by default) is intended to restrict crawling to the same site as the base URL. The implementation used String.startsWith() to compare URLs, which does not perform semantic URL validation. An attacker who controls content on a crawled page could include links to domains that share a string prefix with the target, causing the crawler to follow links to attacker-controlled or internal infrastructure. Additionally, the crawler performed no validation against private or reserved IP addresses. A crawled page could include links targeting cloud metadata services, localhost, or RFC 1918 addresses, and the crawler would fetch them without restriction. This vulnerability is fixed in 1.1.14."
|
||||
},
|
||||
"defaultConfiguration": {
|
||||
"level": "warning"
|
||||
},
|
||||
"helpUri": "https://avd.aquasec.com/nvd/cve-2026-26019",
|
||||
"help": {
|
||||
"text": "Vulnerability CVE-2026-26019\nSeverity: MEDIUM\nPackage: @langchain/community\nFixed Version: 1.1.14\nLink: [CVE-2026-26019](https://avd.aquasec.com/nvd/cve-2026-26019)\nLangChain is a framework for building LLM-powered applications. Prior to 1.1.14, the RecursiveUrlLoader class in @langchain/community is a web crawler that recursively follows links from a starting URL. Its preventOutside option (enabled by default) is intended to restrict crawling to the same site as the base URL. The implementation used String.startsWith() to compare URLs, which does not perform semantic URL validation. An attacker who controls content on a crawled page could include links to domains that share a string prefix with the target, causing the crawler to follow links to attacker-controlled or internal infrastructure. Additionally, the crawler performed no validation against private or reserved IP addresses. A crawled page could include links targeting cloud metadata services, localhost, or RFC 1918 addresses, and the crawler would fetch them without restriction. This vulnerability is fixed in 1.1.14.",
|
||||
"markdown": "**Vulnerability CVE-2026-26019**\n| Severity | Package | Fixed Version | Link |\n| --- | --- | --- | --- |\n|MEDIUM|@langchain/community|1.1.14|[CVE-2026-26019](https://avd.aquasec.com/nvd/cve-2026-26019)|\n\nLangChain is a framework for building LLM-powered applications. Prior to 1.1.14, the RecursiveUrlLoader class in @langchain/community is a web crawler that recursively follows links from a starting URL. Its preventOutside option (enabled by default) is intended to restrict crawling to the same site as the base URL. The implementation used String.startsWith() to compare URLs, which does not perform semantic URL validation. An attacker who controls content on a crawled page could include links to domains that share a string prefix with the target, causing the crawler to follow links to attacker-controlled or internal infrastructure. Additionally, the crawler performed no validation against private or reserved IP addresses. A crawled page could include links targeting cloud metadata services, localhost, or RFC 1918 addresses, and the crawler would fetch them without restriction. This vulnerability is fixed in 1.1.14."
|
||||
},
|
||||
"properties": {
|
||||
"cvssv3_baseScore": 4.1,
|
||||
"cvssv3_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:N/A:N",
|
||||
"precision": "very-high",
|
||||
"security-severity": "4.1",
|
||||
"tags": ["vulnerability", "security", "MEDIUM"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "0.69.1"
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "CVE-2026-26019",
|
||||
"ruleIndex": 0,
|
||||
"level": "warning",
|
||||
"message": {
|
||||
"text": "Package: @langchain/community\nInstalled Version: 0.3.36\nVulnerability CVE-2026-26019\nSeverity: MEDIUM\nFixed Version: 1.1.14\nLink: [CVE-2026-26019](https://avd.aquasec.com/nvd/cve-2026-26019)"
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "bun.lock",
|
||||
"uriBaseId": "ROOTPATH"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 104,
|
||||
"startColumn": 1,
|
||||
"endLine": 104,
|
||||
"endColumn": 1
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"text": "bun.lock: @langchain/community@0.3.36"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user