diff --git a/docs/automations/integrations/checkmarx/label-checkmarx-scan-results/README.md b/docs/automations/integrations/checkmarx/label-checkmarx-scan-results/README.md new file mode 100644 index 000000000..629474d25 --- /dev/null +++ b/docs/automations/integrations/checkmarx/label-checkmarx-scan-results/README.md @@ -0,0 +1,45 @@ +--- +title: Automation - Auto-Label Checkmarx Scan Results +description: Automatically apply labels to PRs that indicate the result of Checkmarx scans. +category: [security, checkmarx] +--- +# Auto-Label Checkmarx Scan Results + + + +Automatically apply labels to PRs that indicate the result of Checkmarx scans. + +!!! warning "Required gitStream Plugin" + This example requires you to [install the `extractCheckmarxFindings` plugin](/filter-function-plugins/#extractcheckmarxfindings). + + [Learn more about gitStream plugins](/plugins/). + +
+!!! info "Configuration Description" + Conditions (all must be true): + + * Checkmarx detects one or more new issues with the code in the PR. + + Automation Actions: + + * Apply a label that indicates which scan type identified the issue (SAST, SCA, or IaC). + +
+
+!!! example "Auto-Label Checkmarx Scan Results" + ```yaml+jinja + --8<-- "docs/downloads/automation-library/integrations/checkmarx/label_checkmarx_scan_results.cm" + ``` +
+ + [:octicons-download-24: Download this example as a CM file.](/downloads/automation-library/integrations/checkmarx/label_checkmarx_scan_results.cm){ .md-button } + +
+
+ + +## Additional Resources + +--8<-- "docs/snippets/general.md" + +--8<-- "docs/snippets/automation-footer.md" diff --git a/docs/automations/integrations/checkmarx/review-checkmarx-alerts/README.md b/docs/automations/integrations/checkmarx/review-checkmarx-alerts/README.md new file mode 100644 index 000000000..08e98fa91 --- /dev/null +++ b/docs/automations/integrations/checkmarx/review-checkmarx-alerts/README.md @@ -0,0 +1,44 @@ +--- +title: Automation - Review Checkmarx Security Alerts +description: Automatically require review from your SecOps team for Checkmarx security violations in pull requests. +category: [security, checkmarx] +--- +# Require Security Review for Checkmarx Alerts + +Automatically require review from your SecOps team for Checkmarx security violations in pull requests. + +!!! warning "Required gitStream Plugin" + This example requires you to [install the `extractCheckmarxFindings` plugin](/filter-function-plugins/#extractcheckmarxfindings). + + [Learn more about gitStream plugins](/plugins/). + +
+!!! info "Configuration Description" + Conditions (all must be true): + + * The PR contains a SAST finding, SCA vulnerability, or IaC issue flagged as Critical or High. + + Automation Actions: + + * Require review from your organization's security team. + * Post a comment explaining the requirement. + +
+
+!!! example "Review Checkmarx Security Alerts" + ```yaml+jinja + --8<-- "docs/downloads/automation-library/integrations/checkmarx/review_checkmarx_alerts.cm" + ``` +
+ + [:octicons-download-24: Download this example as a CM file.](/downloads/automation-library/integrations/checkmarx/review_checkmarx_alerts.cm){ .md-button } + +
+
+ + +## Additional Resources + +--8<-- "docs/snippets/general.md" + +--8<-- "docs/snippets/automation-footer.md" diff --git a/docs/downloads/automation-library/integrations/checkmarx/label_checkmarx_scan_results.cm b/docs/downloads/automation-library/integrations/checkmarx/label_checkmarx_scan_results.cm new file mode 100644 index 000000000..41090c1db --- /dev/null +++ b/docs/downloads/automation-library/integrations/checkmarx/label_checkmarx_scan_results.cm @@ -0,0 +1,28 @@ +# -*- mode: yaml -*- + +manifest: + version: 1.0 + +automations: + {% for item in reports %} + label_checkmarx_{{ item.name }}: + if: + - {{ item.count > 0 }} + run: + - action: add-label@v1 + args: + label: 'checkmarx:{{ item.name }}' + {% endfor %} + +checkmarx: {{ pr | extractCheckmarxFindings }} + +reports: + - name: sast-findings + count: {{ checkmarx.sast.count }} + - name: sca-findings + count: {{ checkmarx.sca.count }} + - name: iac-findings + count: {{ checkmarx.kics.count }} + +colors: + red: 'b60205' diff --git a/docs/downloads/automation-library/integrations/checkmarx/review_checkmarx_alerts.cm b/docs/downloads/automation-library/integrations/checkmarx/review_checkmarx_alerts.cm new file mode 100644 index 000000000..b4f53e403 --- /dev/null +++ b/docs/downloads/automation-library/integrations/checkmarx/review_checkmarx_alerts.cm @@ -0,0 +1,23 @@ +# -*- mode: yaml -*- + +manifest: + version: 1.0 +automations: + review_checkmarx_alerts: + if: + - {{ has.sast_alert or has.sca_alert or has.iac_alert }} + run: + - action: require-reviewers@v1 + args: + reviewers: [my-organization/security-team] + - action: add-comment@v1 + args: + comment: | + This PR requires additional review because Checkmarx identified security issues that need attention. + +checkmarx: {{ pr | extractCheckmarxFindings }} + +has: + sast_alert: {{ checkmarx.sast.severity.critical > 0 or checkmarx.sast.severity.high > 0 }} + sca_alert: {{ checkmarx.sca.severity.critical > 0 or checkmarx.sca.severity.high > 0 }} + iac_alert: {{ checkmarx.kics.severity.critical > 0 or checkmarx.kics.severity.high > 0 }} diff --git a/docs/filter-function-plugins.md b/docs/filter-function-plugins.md index 4b0d0d1ee..ac409f2d4 100644 --- a/docs/filter-function-plugins.md +++ b/docs/filter-function-plugins.md @@ -26,6 +26,8 @@ JavaScript plugins that enable custom filter functions for gitStream. To learn h --8<-- "plugins/filters/extractSnykVersionBump/README.md" +--8<-- "plugins/filters/extractCheckmarxFindings/README.md" + --8<-- "plugins/filters/extractOrcaFindings/README.md" --8<-- "plugins/filters/generateDescription/README.md" diff --git a/docs/integrations/README.md b/docs/integrations/README.md index a08d86a43..ede340a7e 100644 --- a/docs/integrations/README.md +++ b/docs/integrations/README.md @@ -95,6 +95,12 @@ visible: false
+
+
+[:material-shield-search: Checkmarx](/integrations/checkmarx/) +
+
+
![Orca Security](../downloads/images/Orca-Mark-Black.png#only-light) ![LinearB](../downloads/images/Orca-Mark-White.png#only-dark) Orca diff --git a/docs/integrations/checkmarx.md b/docs/integrations/checkmarx.md new file mode 100644 index 000000000..bc3bb4578 --- /dev/null +++ b/docs/integrations/checkmarx.md @@ -0,0 +1,19 @@ +--- +title: Integrate gitStream with Checkmarx +description: Implement workflow automations for Checkmarx. +--- +# Integrate gitStream with Checkmarx + +## Auto-Label Checkmarx Scan Results +--8<-- "docs/automations/integrations/checkmarx/label-checkmarx-scan-results/README.md:example" + +## Require Security Review for Checkmarx Alerts +--8<-- "docs/automations/integrations/checkmarx/review-checkmarx-alerts/README.md:example" + + + +## Additional Resources + +--8<-- "docs/snippets/general.md" + +--8<-- "docs/snippets/automation-footer.md" diff --git a/plugins/filters/extractCheckmarxFindings/LICENSE b/plugins/filters/extractCheckmarxFindings/LICENSE new file mode 100644 index 000000000..5d3cca0ac --- /dev/null +++ b/plugins/filters/extractCheckmarxFindings/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 LinearB + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/filters/extractCheckmarxFindings/README.md b/plugins/filters/extractCheckmarxFindings/README.md new file mode 100644 index 000000000..23fa27376 --- /dev/null +++ b/plugins/filters/extractCheckmarxFindings/README.md @@ -0,0 +1,24 @@ +--8<-- "plugins/filters/extractCheckmarxFindings/reference.md" + +Usage example, that adds labels based on Checkmarx scan findings. + +??? note "Plugin Code: extractCheckmarxFindings" + ```javascript + --8<-- "plugins/filters/extractCheckmarxFindings/index.js" + ``` +
+ + +
+ + +??? example "gitStream CM Example: extractCheckmarxFindings" + ```yaml+jinja + --8<-- "plugins/filters/extractCheckmarxFindings/extract_checkmarx_findings.cm" + ``` +
+ + +
+ +[Download Source Code](https://github.com/linear-b/gitstream/tree/main/plugins/filters/extractCheckmarxFindings) diff --git a/plugins/filters/extractCheckmarxFindings/extract_checkmarx_findings.cm b/plugins/filters/extractCheckmarxFindings/extract_checkmarx_findings.cm new file mode 100644 index 000000000..41090c1db --- /dev/null +++ b/plugins/filters/extractCheckmarxFindings/extract_checkmarx_findings.cm @@ -0,0 +1,28 @@ +# -*- mode: yaml -*- + +manifest: + version: 1.0 + +automations: + {% for item in reports %} + label_checkmarx_{{ item.name }}: + if: + - {{ item.count > 0 }} + run: + - action: add-label@v1 + args: + label: 'checkmarx:{{ item.name }}' + {% endfor %} + +checkmarx: {{ pr | extractCheckmarxFindings }} + +reports: + - name: sast-findings + count: {{ checkmarx.sast.count }} + - name: sca-findings + count: {{ checkmarx.sca.count }} + - name: iac-findings + count: {{ checkmarx.kics.count }} + +colors: + red: 'b60205' diff --git a/plugins/filters/extractCheckmarxFindings/index.js b/plugins/filters/extractCheckmarxFindings/index.js new file mode 100644 index 000000000..0e61b62de --- /dev/null +++ b/plugins/filters/extractCheckmarxFindings/index.js @@ -0,0 +1,91 @@ +/** + * @module extractCheckmarxFindings + * @description Extract security findings from Checkmarx PR comments + * @param {Object} pr - the gitStream's PR context variable + * @returns {Object} Findings + * Findings.sast: { count: null, severity: '' }, + * Findings.sca: { count: null, severity: '' }, + * Findings.kics: { count: null, severity: '' }, + * @example {{ pr | extractCheckmarxFindings }} + * @license MIT +**/ + +const SCANNERS = ['sast', 'sca', 'kics']; +const SEVERITY_LEVELS = ['critical', 'high', 'medium', 'low', 'info']; + +function emptySeverity() { + return { critical: 0, high: 0, medium: 0, low: 0, info: 0 }; +} + +function emptyFindings() { + return { count: null, severity: '' }; +} + +function detectScanner(line) { + if (/\b(IaC[\s-]*Security|KICS)\b/i.test(line)) return 'kics'; + if (/\bSAST\b/i.test(line)) return 'sast'; + if (/\bSCA\b/i.test(line)) return 'sca'; + return null; +} + +function parseCheckmarxComment(content) { + const findings = {}; + SCANNERS.forEach(s => { findings[s] = emptyFindings(); }); + + const severityCounts = {}; + SCANNERS.forEach(s => { severityCounts[s] = emptySeverity(); }); + + const lines = content.split('\n'); + let currentScanner = null; + let inNewIssues = false; + + for (const line of lines) { + // Track whether we're in a "New Issues" or "Fixed Issues" section. + // Only count findings from the "New Issues" section. + if (/\bNew\s+Issues\b/i.test(line)) { + inNewIssues = true; + } else if (/\bFixed\s+Issues\b/i.test(line)) { + inNewIssues = false; + } + + // Update current scanner section when we see a scanner name + const scanner = detectScanner(line); + if (scanner) { + currentScanner = scanner; + } + + // Match table rows where the first cell contains a severity level + // Checkmarx tables use: | SEVERITY | Issue | ... | + if (!inNewIssues) continue; + const tableMatch = line.match(/^\s*\|\s*(CRITICAL|HIGH|MEDIUM|LOW|INFO)\b/i); + if (tableMatch && currentScanner) { + const level = tableMatch[1].toLowerCase(); + severityCounts[currentScanner][level]++; + } + } + + SCANNERS.forEach(s => { + const counts = severityCounts[s]; + const total = counts.critical + counts.high + counts.medium + counts.low + counts.info; + findings[s] = { count: total, severity: counts }; + }); + + return findings; +} + +module.exports = (pr) => { + let checkmarxObject = {}; + SCANNERS.forEach(s => { checkmarxObject[s] = emptyFindings(); }); + + // Checkmarx comments can appear as PR comments or reviews + const checkmarxComment = pr.comments + .filter(x => x.commenter.includes('checkmarx')) + .concat(pr.reviews.filter(x => x.commenter.includes('checkmarx'))); + + if (checkmarxComment.length) { + const latestComment = checkmarxComment[checkmarxComment.length - 1].content; + checkmarxObject = parseCheckmarxComment(latestComment); + } + + return JSON.stringify(checkmarxObject); +} diff --git a/plugins/filters/extractCheckmarxFindings/reference.md b/plugins/filters/extractCheckmarxFindings/reference.md new file mode 100644 index 000000000..9c52249fc --- /dev/null +++ b/plugins/filters/extractCheckmarxFindings/reference.md @@ -0,0 +1,19 @@ + + +## extractCheckmarxFindings +Extract security findings from Checkmarx PR comments + +**Returns**: Object - Findings +Findings.sast: { count: null, severity: '' }, +Findings.sca: { count: null, severity: '' }, +Findings.kics: { count: null, severity: '' }, +**License**: MIT + +| Param | Type | Description | +| --- | --- | --- | +| PR | Object | the gitStream's PR context variable | + +**Example** +```js +{{ pr | extractCheckmarxFindings }} +```