Add audit log events for Dependency Firewall license policy decisions

What does this MR do and why?

Adds audit event logging to the Dependency Firewall so that every policy decision — allowed, blocked, or advisory (warned) — is recorded as a project-scoped audit event.

Three new audit event types are introduced:

Event Triggered when
dependency_firewall_allowed No active policy matched; dependency is permitted
dependency_firewall_blocked An enforced policy matched; dependency is blocked
dependency_firewall_warned An advisory policy matched; dependency is allowed but flagged

Each event includes additional_details with the purl, operation type (package_download, package_upload, container_pull, container_push), the matched policy name, and the matched rule (including denied license names for license rules).

Implementation

  • CreateAuditEventsService — new service responsible for building and dispatching audit events. Isolated from enforcement logic so it can be tested and extended independently.
  • EnforcementService#enforce_policy — calls audit before each return path so no decision can escape logging.
  • Audit event YML definitions added for all three types.
  • Documentation added to the audit event types reference page.

References

https://gitlab.com/gitlab-org/gitlab/-/work_items/588486+s

Screenshots or screen recordings

image

How to set up and validate locally

  1. Start GDK and enable the feature flag

    # Rails console
    Feature.enable(:dependency_firewall_phase1)
  2. Configure a security policy project with a DFW policy containing at least one enforced rule and one advisory rule, e.g.:

    dependency_firewall_policy:
      - name: Block Copyleft
        enabled: true
        enforcement_type: enforced
        rules:
          - type: license
            denied:
              - name: GPL-3.0
      - name: Advisory
        enabled: true
        enforcement_type: advisory
        rules:
          - type: license
            denied:
              - name: Apache-2.0

    After saving the policy YAML, persist it:

    config = SecurityOrchestrationPolicyConfiguration.last
    Security::PersistSecurityPoliciesWorker.new.perform(config.id)
  3. Trigger a package download against a project linked to the policy (e.g. via curl or mvn dependency:get). Use packages with known licenses to exercise each path:

    • MIT licence → expect dependency_firewall_allowed
    • Apache-2.0 → expect dependency_firewall_warned
    • GPL-3.0 → expect dependency_firewall_blocked + 403 response
  4. Verify audit events appear in the UI at Security > Audit Events on the project, or via the API:

    curl --header "PRIVATE-TOKEN: <token>" \
      "http://gdk.test:3000/api/v4/projects/<id>/audit_events"

    Each event should include purl, operation, policy_name, and matched_rule in its details.

  5. Verify the flag is respected — disable the flag and confirm no audit events are created:

    Feature.disable(:dependency_firewall_phase1)

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #588486

Edited by Hannah Baker

Merge request reports

Loading