Vulnerability Management

Pipeline-Triggered Scans With CI/CD and Webhooks

By PMAP Security Team 19 min read

Most vulnerability programs still treat scanning as a batch job. A scanner runs overnight, findings land in a queue the next morning, and by the time anyone looks at them the code has already merged, deployed, and moved on. For an infrastructure scan on a slow-changing estate that cadence is fine. For application security, where a developer can introduce and ship a vulnerable dependency in a single afternoon, it is a generation too late.

CI/CD security scanning closes that gap by moving the trigger point. Instead of waiting for a scheduled sweep, the pipeline itself tells your vulnerability management platform that a change has entered review, and the platform reacts in the moment. A push to a protected branch fans out to the right code scanners. A pull request gets a pass, warn, or block verdict before anyone clicks merge. Every finding carries the exact commit that introduced it. This is what shifting findings left actually looks like in practice, and it depends on a small set of plumbing decisions getting made correctly.

This article walks through how PMAP wires pipeline events to vulnerability management, why each design choice exists, and what good looks like when you connect a repository. It sits inside the broader security integration layer that unifies every scanner, ticketing system, and pipeline PMAP talks to. If you want the click-by-click setup instead of the reasoning behind it, the guide to wiring CI/CD pipelines for pipeline-triggered scans covers the configuration steps.

Why Nightly Batches Are Too Late for DevSecOps

The problem with a nightly scan is not that it is slow. It is that it answers the wrong question at the wrong moment. A nightly batch tells you what is wrong with code that already shipped. A DevSecOps workflow needs to know what is wrong with code that is about to ship, while the author still has context loaded and a reviewer is still looking at the diff.

The cost of late feedback is well documented. A vulnerable change caught at review time costs a developer a few minutes to fix in the same branch. The same change caught a week later in a batch scan costs a context switch, a fresh investigation, a new branch, a new review, and often a production hotfix. The defect did not get more severe. The remediation got more expensive because the change moved further down the delivery line. The OWASP DevSecOps Guideline frames this as embedding security feedback into the developer’s existing flow rather than bolting it on afterward.

PMAP addresses the timing problem by treating CI events as first-class triggers. When a pipeline emits a push or pull-request event, PMAP receives it directly, decides whether a scan is warranted, runs the relevant code scanners, correlates the results, and reports a verdict back to the platform the developer is already using. The data surfaces at change-review time, not hours later from a batch. That single shift in timing is what makes the rest of the workflow worth building.

Six CI/CD Connectors, One Webhook Endpoint

PMAP ships CI/CD connectors for six platforms: GitHub, GitLab, Jenkins, Azure DevOps, Bamboo, and Bitbucket. These cover the large majority of source-control and pipeline tooling in enterprise environments, from cloud-hosted Git platforms to self-managed build servers.

What matters more than the count is the architecture behind it. Every one of these vendors delivers events to the same inbound endpoint, /api/v1/webhooks/ci/{vendor}/{integrationId}. The vendor name and the integration identity are encoded in the path, so a single receiver knows which connector configuration and which credentials apply to an incoming event without any guesswork. You configure the connector once from the vendor marketplace, copy the webhook URL and secret into the source platform, and events begin flowing.

This uniformity is deliberate. The platforms differ wildly in payload shape, header conventions, and signing schemes. GitHub sends one JSON structure, GitLab another, Jenkins a third. If those differences leaked into PMAP’s scanning, correlation, and gating logic, every downstream feature would need six code paths. Instead the differences are absorbed at the edge, which keeps the rest of the system vendor-neutral. We will return to how that normalization works after covering the security boundary, because the first thing any inbound webhook has to do is prove it is genuine.

Inbound Webhook Security Per Vendor

A webhook endpoint is a publicly reachable URL that triggers privileged work inside your platform. If it accepts anonymous traffic, anyone who learns the URL can forge events, trigger scans, or poison your audit log. So before PMAP does anything with an inbound CI event, it verifies that the event came from the platform it claims to come from.

Each vendor gets a verification scheme matched to what that vendor actually supports. GitHub signs every delivery with an X-Hub-Signature-256 header, an HMAC-SHA256 of the payload computed with the shared secret, and PMAP recomputes and compares that signature. GitLab uses an X-Gitlab-Token header checked with a constant-time comparison. Azure DevOps uses a Bearer token, also compared in constant time. Jenkins, Bamboo, and Bitbucket use an X-Api-Token, a UUID, or Bitbucket’s own signature, each verified in constant time. The constant-time comparison matters because it prevents timing attacks that could otherwise leak the secret one byte at a time.

HMAC verification is the strongest of these schemes and the reason GitHub deliveries are hardest to forge. The signature proves both that the sender holds the secret and that the payload was not altered in transit, because any change to the body changes the computed hash. The GitHub Checks API documentation and the broader OWASP CI/CD Security Cheat Sheet both treat signed webhook verification as a baseline control rather than an optional hardening step.

Unsigned Hooks Rejected on Production

There is exactly one way to turn off signature enforcement, and it is gated by an explicit environment flag. When a production instance has no secret configured and CI_WEBHOOK_ALLOW_UNSIGNED is not set to true, every inbound CI hook is rejected outright. There is no quiet fallback to accepting unsigned events.

The unsigned mode exists for a narrow reason. During local development you may want to fire test events without setting up signing first. That convenience is fine on a laptop and dangerous in production, where disabling verification would open the endpoint to anyone. PMAP makes the trade-off explicit. You have to consciously set the flag, and the default posture is to refuse anything it cannot verify. That default is the right one for any internet-reachable instance, and it is the behavior you should leave in place.

CIEvent Normalization Across Vendors

Once an inbound event passes verification, PMAP maps it onto a single canonical structure called a CIEvent. Whether the source was a GitHub pull request, a GitLab merge request, a Jenkins build, or an Azure DevOps push, the orchestrator downstream only ever sees a CIEvent. The vendor-specific payload differences stop at this boundary.

This is the design decision that lets one endpoint serve six platforms without the rest of the system caring which one fired. Branch filtering, auto-scan fan-out, PR gating, and audit logging all operate on the normalized event, so each of those features is written once rather than six times. When PMAP adds a seventh CI platform later, the work is confined to the receiver and the mapping into a CIEvent. Nothing downstream has to change.

Normalization also makes traceability uniform. Fields like the commit SHA, branch name, and pull-request number are populated from the normalized event regardless of which vendor produced them. That means a finding traced back to its source commit looks the same whether the change came through GitHub or Bitbucket. Where a vendor genuinely lacks a field, a Jenkins build with no pull-request context for example, the corresponding field is simply left blank rather than faked. Honest absence beats invented data when an analyst is trying to trust a trail.

The Auto-Scan Orchestrator and Branch Filters

Not every push deserves a scan. A developer pushing twenty commits to a throwaway feature branch should not trigger twenty full code scans, and the noise from scanning every branch would drown the signal you actually care about. The auto-scan orchestrator solves this by evaluating a branch filter before it does anything else.

You configure the branch filter on the Auto-Scan tab of the integration’s detail page, alongside the target scanners and the gate thresholds. When a CIEvent arrives, the orchestrator checks the event’s branch against the filter. If the branch does not match, the webhook is acknowledged with an HTTP 200, but no scan is triggered and no scanner fan-out happens. The event is recognized and dropped on purpose. This keeps scanning focused on the branches that matter, typically your protected and release branches, rather than every transient working branch.

When the filter passes, the orchestrator fans out. It fires pipeline triggers and launches scans on the SAST and SCA target integrations you configured, the static analysis and software-composition scanners that actually inspect the code and its dependencies. Each target is triggered independently. The fan-out is fire-and-forget per target, so a failed trigger to one scanner does not block triggers to the others. Errors are collected and written to the audit log rather than retried inline, and the inbound webhook still returns 200 immediately so the source platform is never left waiting on PMAP’s downstream work.

Runbook Events Fire Regardless of Auto-Scan

There is a second output from the orchestrator that does not depend on auto-scan being enabled at all. Whenever a matching CI event arrives, the orchestrator always emits a runbook trigger event. This holds even if you have not configured any auto-scan targets.

The separation is useful. Auto-scan answers the question of whether to run scanners. The runbook event answers a different question, which is whether anything else in your automation should react to this pipeline activity. You might want a notification on every push to a production branch, or a custom workflow that fires when a specific repository sees a pull request, independent of scanning. By always emitting the runbook trigger, PMAP lets your runbook rules respond to CI activity on their own terms. Scanning and reaction are decoupled, so you can use one without committing to the other.

The PR Security Gate: Block, Warn or Pass

The most visible piece of this whole workflow is the verdict that lands back on the pull request. After a triggering change has been scanned and its findings correlated, PMAP reports a commit-check status to the source platform, and that status is what a developer and reviewer see next to the merge button.

The gate has three outcomes, driven by two configurable severity lists. Severities in the BlockOnNew list cause the commit check to be set to failure, which is the hard stop that signals the merge should not proceed. Severities in the WarnOnNew list set the check to pending, a softer signal that flags risk without slamming the door. A clean result, where nothing crosses either threshold, sets the check to success and posts a passing security-summary comment to the pull request or merge request. The developer gets the verdict inside the tool they are already in, with no context switch into a separate security console.

The single most important property of this gate is what it evaluates. It looks only at *new* findings introduced by the triggering change. It does not re-litigate the entire backlog of pre-existing issues in the repository every time someone opens a pull request. If the gate blocked on the cumulative debt of a legacy codebase, every merge would fail and the team would route around the control within a week. By scoping the verdict to the delta, the gate stays honest. It blocks the change that made things worse and stays quiet about the change that did not. That framing, gating on regression rather than on absolute state, is exactly the discipline the NIST Secure Software Development Framework, SP 800-218 describes for integrating security checks into the build and review process.

You tune the two lists to your team’s risk tolerance. A team early in its DevSecOps journey might block only on new critical findings and warn on high, so the gate has teeth without grinding delivery to a halt. A mature team running a hardened codebase might block on critical and high both. The thresholds are policy, and you own the policy.

Commit-to-Finding Traceability

A finding is only as actionable as the context attached to it. “There is a SQL injection in this repository” is a starting point. “There is a SQL injection introduced by commit a1b2c3d on the payments-refactor branch in pull request 482” is something a developer can act on in minutes.

PMAP writes that context onto the scan record at the moment the CI event triggers the scan. Five fields capture the source of every pipeline-triggered scan: commit_sha, branch_name, pr_number, repo_url, and triggered_by. Because these are recorded at trigger time and flow through to the findings the scan produces, any finding from a CI-triggered scan can be traced straight back to the change that caused it. There is no manual cross-referencing between a scanner report and a Git history.

This traceability pays off in two directions. For remediation, it points the owner at the exact commit and pull request, so the fix lands in the right place fast. For audit and governance, it produces a defensible chain of evidence linking every finding to a specific change, a specific branch, and a specific person or system that triggered the scan. When an auditor asks how a vulnerability entered the codebase and how it was caught, the answer is in the record rather than in someone’s memory. Where a vendor omits a field, a build event with no associated pull request for instance, the field is left blank rather than guessed, so the trail stays trustworthy.

The CI Event Audit Log

Webhook delivery is the kind of thing that works flawlessly until it silently stops. A rotated secret, a firewall change, a misconfigured URL, any of these can break delivery, and without visibility you would not know until findings mysteriously stopped appearing. PMAP gives every CI integration a built-in audit log so you can see exactly what arrived and what happened to it.

The CI Events tab on the integration detail page shows the last 200 webhook events for that connector. Each row records the event type, the repository, the branch, the commit SHA, the pull-request number, the action PMAP took in response, and a signature-validity badge. That last column is the one you reach for when something is wrong. If events are arriving but showing as signature-invalid, you have a secret mismatch, usually because the secret was rotated on one side but not the other. If events are not arriving at all, the problem is upstream of PMAP, in the source platform’s webhook configuration or the network path between the two.

The 200-event window is a fixed cap rather than a configurable retention setting. It is sized for live troubleshooting, letting you see recent delivery behavior without external log access, not for long-term forensic archival. Older events age out and are not replayable. If your compliance posture requires durable retention of every CI event, you would forward the relevant data into your own log store, and the audit log gives you the recent window you need to diagnose problems as they happen.

Secret Rotation Without a Grace Window

Rotating a webhook secret is a routine security hygiene task, and PMAP makes it deliberately strict. When you rotate the secret on a CI integration, the previous value is invalidated immediately. There is no grace period during which both the old and new secret are accepted.

This is a security choice with an operational consequence, and it is worth understanding before you rotate. The strictness is correct, because a grace window is exactly the gap an attacker would exploit, continuing to forge events with a leaked secret while a new one comes online. By cutting the old secret the moment a new one is generated, PMAP leaves no overlap to abuse. The consequence is that rotation is a coordinated change. You generate the new secret in PMAP, then update the source platform’s webhook configuration with it, and any event signed with the old secret in the interim is rejected. In practice this means rotating during a quiet window and updating both sides promptly. The behavior is the same one you should expect from any system that takes signature verification seriously, and it is the same discipline PMAP applies to its connector and credential management across every integration type.

How PMAP Shifts Findings Left Without Slowing Developers

The promise of shift-left is easy to state and hard to deliver without making developers resent the tooling. The failure mode is a gate that blocks too much, scans that take too long, or feedback that lands in a console no developer ever opens. Each of those turns a security control into an obstacle people route around. PMAP’s CI/CD design avoids each one through specific choices rather than good intentions.

It scans only what matters, because the branch filter keeps fan-out focused on protected and release branches instead of every working branch. It blocks only what got worse, because the PR gate evaluates new findings rather than the entire backlog, so a legacy codebase does not fail every merge. It returns control to the developer immediately, because the inbound webhook responds with a 200 the moment it is verified and all downstream scanning happens out of band, so the pipeline is never held hostage to PMAP’s processing. And it delivers the verdict where developers already work, as a commit-check status and a pull-request comment, so no one has to leave their code-review tool to learn whether a change is safe to merge.

Underneath all of it sits the correlation engine that every PMAP ingest path feeds. CI-triggered scans are not a separate findings silo. They flow into the same deduplicated, correlated finding set as your scheduled scans and file imports, carrying their commit context with them. The pipeline becomes one more source of truth that converges with the rest, which is the whole point of treating CI/CD as part of the integration layer rather than a bolt-on. For teams formalizing this practice, the discipline connects directly to broader shift-left DevSecOps strategy, where pipeline-triggered scanning is one control among several.

The result is a security gate that developers experience as fast, scoped, and in-context, and that security teams experience as governed, signed, and fully audited. That balance, security without friction, is what makes pipeline-triggered scanning stick rather than get disabled the first time it slows a release.

Frequently Asked Questions

Which CI/CD platforms does PMAP integrate with?

PMAP ships CI/CD connectors for six platforms: GitHub, GitLab, Jenkins, Azure DevOps, Bamboo, and Bitbucket. All six deliver events to a single inbound endpoint and are normalized into one canonical event structure, so the scanning, gating, and audit logic is identical regardless of which platform fired the event.

How does PMAP verify that a CI webhook is genuine?

Each vendor uses a verification scheme matched to what it supports. GitHub uses an X-Hub-Signature-256 HMAC-SHA256 signature, GitLab uses a constant-time X-Gitlab-Token check, Azure DevOps uses a Bearer token, and Jenkins, Bamboo, and Bitbucket use an X-Api-Token, a UUID, or Bitbucket’s own signature, each compared in constant time. On a production instance with no secret configured, every inbound hook is rejected unless CI_WEBHOOK_ALLOW_UNSIGNED is explicitly set to true, a mode intended only for local development.

What does the PR security gate actually block on?

The gate posts a commit-check status of failure, pending, or success based on two configurable severity lists. New findings at a severity in the BlockOnNew list set the check to failure, severities in WarnOnNew set it to pending, and a clean result sets it to success with a passing security-summary comment. The gate evaluates only the new findings introduced by the triggering change, not the repository’s entire existing backlog, so merges fail on regression rather than on legacy debt.

Does every push trigger a scan?

No. The auto-scan orchestrator evaluates a configurable branch filter before doing anything. Events on branches that do not match the filter are acknowledged with an HTTP 200 but trigger no scan and no scanner fan-out. This keeps scanning focused on the branches you care about, typically protected and release branches, rather than every transient working branch.

Can I trace a finding back to the commit that introduced it?

Yes. When a CI event triggers a scan, PMAP writes commit_sha, branch_name, pr_number, repo_url, and triggered_by onto the scan record at trigger time. Those fields flow through to the findings the scan produces, so any finding from a CI-triggered scan can be traced directly to the change, branch, and pull request that caused it. Where a vendor omits a field, such as a build with no pull-request context, the field is left blank rather than guessed.

What happens when I rotate a webhook secret?

Rotation invalidates the previous secret immediately, with no grace window during which both values are accepted. This prevents an attacker from continuing to use a leaked secret, but it means you must update the source platform’s webhook configuration with the new secret promptly, because any event still signed with the old value will be rejected. Rotate during a quiet window and update both sides together.

How do I troubleshoot webhook deliveries that are not arriving?

Open the CI Events tab on the integration detail page, which shows the last 200 webhook events with event type, repository, branch, commit, pull-request number, action taken, and a signature-validity badge. If events arrive but show as signature-invalid, you have a secret mismatch, usually a secret rotated on one side but not the other. If no events appear at all, the problem is upstream in the source platform’s webhook configuration or the network path. The 200-event window is a fixed cap sized for live troubleshooting rather than long-term archival.

author avatar
PMAP Security Team

Newsletter

Get the next writeup in your inbox

One short email when a new case writeup or detection deep dive ships. No marketing drip, no third-party tracking.