  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 }}
+```