Checkmarx is a solid scanner, but its built-in notification options for critical findings were too passive for our team's SLA. We needed a hard trigger to our on-call rotation, not just another email that gets buried. The API is decent enough to build on, so I wrote a small service to bridge that gap.
The core idea is simple: a lightweight webhook listener that filters for high-severity issues and pushes them directly to PagerDuty as incidents. I set it up as a containerized service listening on a dedicated path. When Checkmarx posts a scan result, the service parses the JSON, looks for severity "High" or above, and if found, formats a PagerDuty event with the project name, finding ID, and vulnerability type. It uses the PagerDuty Events API v2.
The key was making it resilient and dumb. It doesn't store state; it just transforms and forwards. We deployed it internally on a small instance, added its URL to the Checkmarx webhook configuration, and that was it. Now, a critical SAST finding wakes someone up the same way a production server alert would. It forces immediate triage.
If you're running Checkmarx in a high-velocity environment, this kind of integration is non-negotiable. The tool gives you the data, but you have to pipe it into your actual operations workflow. Don't let critical security findings live only inside the security tool.
been there, migrated that
Totally get the push for a "hard trigger" instead of a passive notification. We hit the same wall, but with Salesforce alerts buried in Chatter instead of email. That gentle nudge just doesn't cut it when a pipeline integration breaks at 2 AM.
Your "resilient and dumb" approach is spot on. I've over-engineered those bridges before, adding state and retry logic that just created a second thing to monitor. The one caveat I'd add from my scars: make sure your webhook listener's logs are painfully clear on what it *didn't* forward. We once had a silent filter failure because a severity field changed from "Critical" to "Severe" in the source system. A quick log line for each processed event saved us later.
Forcing immediate triage by routing to the on-call rotation is the only way to make security findings feel as real as infra alerts. Nice build.
The log clarity point is critical, and it extends beyond just filtering. You also need visibility into the PagerDuty API response. Log the incident key on success, and the full error on failure. Otherwise you're flying blind on half the integration.
We had a similar issue when a vendor "updated" their payload and nested the severity field. The listener logged the received event, but the key field was missing so nothing fired. Took a week to notice.
Your note about making infra and security alerts feel the same is the real win. Once a critical finding pages the same engineer as a server outage, it finally gets the right priority.
Logging the PagerDuty incident key is such a good practice. It's the only way to create a traceable audit trail from the original finding all the way to the opened ticket. Without that key, you're left manually correlating timestamps when something goes sideways.
That vendor payload change story is a classic. It makes me think we should treat these integration listeners as having their own schema contracts. A quick JSON Schema validation step right at ingestion, logged as a pass/fail, could catch those nested field shifts immediately. It adds a tiny bit of complexity, but it's still in the "dumb pipe" spirit if it's just a validation block.
And you're right, the cultural shift of security alerts paging like infra is huge. We saw the same when we started pushing high-risk Salesforce data leaks into PagerDuty - response times dropped by 80% because it was no longer a "check the report on Monday" item.
Your example about the nested severity field is exactly why I advocate for explicit, versioned integration tests in CI for these little pipes. Even a dumb forwarder has an implicit schema. We run a weekly job that replays a library of known-good payloads (and a few known-bad ones) against a staging instance of the listener. It catches those vendor "updates" before they hit production.
Logging the incident key is necessary, but I'd add it's not sufficient for the audit trail. We also log the deduplication key we send to PagerDuty, which is a hash of the source finding ID and project. That way, even if the PagerDuty-side incident key is lost from our logs, we can reconstruct the intended action.
You're dead right about the cultural shift. We found the final step was having the on-call engineer who gets paged be the one to acknowledge and then immediately create the Jira ticket from the incident. It creates a direct, single-threaded handoff. No more "some system created a ticket" that gets deprioritized.