How to Manage Policy Precedence and Exception Workflows

Advanced 25 minutes Security Engineers / DevOps Engineers Advanced Configuration

Understand first-match-wins evaluation, order policies by precedence, create time-bound exceptions, and balance security with developer productivity.

Overview

Chainsaw evaluates policies in precedence order using a first-match-wins model. When a package request arrives, Chainsaw walks the policy list from highest to lowest precedence and applies the first policy whose conditions match. Understanding this model is essential for building a layered security posture that balances protection with developer productivity.

Prerequisites

  • Manager or Admin role in Chainsaw
  • Existing policies configured (see earlier tutorials)
  • Understanding of your organization’s security requirements

Step 1: Understand First-Match-Wins Evaluation

Policies are evaluated top to bottom. The first policy whose conditions match the request determines the outcome.

Request: npm install lodash@4.17.21

Policy 1 (Precedence: 100): Allow — package = "lodash"        → MATCH → ALLOW
Policy 2 (Precedence: 90):  Block — CVSS >= 7.0               → (never reached)
Policy 3 (Precedence: 80):  Block — trust_score < 30           → (never reached)
Policy precedence evaluation flow
Policies are evaluated from highest to lowest precedence — first match wins
A broad “Allow” policy at high precedence can silently override all blocking policies below it. Be very deliberate about what gets high precedence.

Step 2: Design Your Policy Stack

A well-designed policy stack typically follows this pattern (highest to lowest precedence):

PrecedencePolicy TypePurpose
100Emergency allowSpecific packages needed urgently during incidents
90Exception allowsTime-bound exceptions for approved risk acceptance
80Critical blocksBlock confirmed malware (trust score = -100)
70High-severity blocksBlock critical vulnerabilities (CVSS >= 9.0)
60Typosquat blocksBlock suspected typosquats
50License blocksBlock prohibited licenses (AGPL, GPL)
40Freshness guardsBlock packages < 14 days old
30Trust score blocksBlock low trust score packages (< 30)
20Quarantine rulesQuarantine moderate-risk packages for review
10Default allowAllow everything that passes all checks
Recommended policy stack layout
A layered policy stack from emergency overrides down to default behavior

Step 3: Reorder Policies

Navigate to Policies. Drag and drop policies to adjust their precedence order.

Dragging policies to reorder
Drag policies up or down to change their evaluation precedence

Rules for Ordering

  1. Specific allows before general blocks — Exceptions need higher precedence than the rules they override
  2. Narrow scope before broad scope — A policy targeting a specific package should be above one targeting all packages
  3. Malware blocks at the top — Never let an exception override malware detection
  4. Default behavior at the bottom — The catch-all policy goes last

Step 4: Create Time-Bound Exceptions

When a developer needs a blocked package, create a time-bound exception:

From the Violations Page

  1. Find the violation in the violations list
  2. Click Create Exception
  3. Set the details:
    • Package: Pre-filled from the violation
    • Version: Specific version or version range
    • Expiry: 30, 60, or 90 days
    • Reason: Document the business justification
Creating an exception from a violation
Create an exception directly from the violation detail

As a Policy

For more control, create the exception as an Allow policy:

  1. Name: Exception: lodash@4.17.20 (JIRA-1234)
  2. Action: Allow
  3. Conditions: Package = lodash, Version = 4.17.20
  4. Scope: Specific client or group
  5. Precedence: Above the blocking policy but below malware blocks
Exception created as an Allow policy
Create a specific Allow policy that overrides the blocking rule
Always include a ticket reference (JIRA, Linear, etc.) in the exception name so the approval is traceable.

Step 5: Exception Lifecycle Management

Track Active Exceptions

Monitor active exceptions from the Overview dashboard. The exceptions KPI shows how many are currently active.

Active exceptions count
Track the number of active exceptions across your organization

Review Expiring Exceptions

Set a regular cadence (weekly or biweekly) to review:

  • Exceptions approaching expiry
  • Whether the underlying issue has been resolved (patched version available)
  • Whether the exception should be renewed or removed

Automatic expiry reminders (T-7d / T-1d / T-0)

Chainsaw runs an idempotent reminder loop against the exceptions table. For every exception, three notifications fire automatically:

TriggerWhenAudience
T-7dSeven days before expiryThe owner email on the exception (or the CODEOWNERS-resolved team if blank)
T-1dOne day before expirySame recipient list
T-0At expiry, when the exception transitions from active to expiredSame recipient list, plus the policy admins group

The dispatch is idempotent on (exception_id, milestone) so a control-plane restart never double-sends. Delivery uses your configured outbound webhook destinations — the same channels as violation notifications. There is no opt-out at the exception level; if you do not want a reminder, do not create the exception (or set the expiry to a date that aligns with your review cadence).

The view at /reports/exceptions/expiring shows everything currently in the seven-day window in one screen, sorted by remaining days, so the weekly review can happen against a single page rather than per-exception.

Exception Renewal vs Removal

ScenarioAction
Patched version availableRemove exception, upgrade the package
No patch yet, risk acceptedRenew with new expiry and updated justification
Risk assessment changedRemove exception, enforce the block
Package no longer neededRemove exception, clean up BOM

Step 6: Scoping Policies for Different Environments

Use policy scoping to apply different rules to different contexts:

By Client Type

Production (service-token):  Block CVSS >= 7.0
Development (end-user):      Quarantine CVSS >= 9.0 (more permissive)

By Repository

npm:     Block trust score < 40 (high attack surface)
maven:   Block trust score < 20 (more stable ecosystem)

By Client Group

frontend-team:  Block all non-MIT/Apache licenses
backend-team:   Allow LGPL (linking is acceptable)
Policies with different scopes
Different policy stacks for different teams and environments

Step 7: Testing Policy Changes

Before applying a new policy to production:

  1. Start with Monitor mode — The policy records Flagged audit entries on every match but never returns a 403. See How to Enable Monitoring for a Single Policy.
  2. Watch the violations view for a week — Check how many requests would be affected and which clients are hitting it.
  3. Review false positives — Create exceptions for legitimate packages before switching to Block.
  4. Switch to Block — Once you’re confident in the scope, change the policy’s mode to block.
Use Billy to forecast impact: “How many packages would be blocked if we set a trust score threshold of 40?”

Common Anti-Patterns

Anti-PatternProblemFix
Broad Allow at topOverrides all blocksScope Allow policies to specific packages only
No default at bottomAmbiguous behavior for unmatched requestsAdd a catch-all Allow or Block at lowest precedence
Expired exceptions left activeAccumulating riskSet expiry dates, review regularly
Copy-paste policiesRedundant rules, hard to maintainConsolidate similar policies
Blocking without monitoring firstDeveloper disruptionSet mode: monitor on the policy first, measure impact, then flip to block
Flipping the global Blocking toggle off to test one policyDisables enforcement on every Block policyUse per-policy Monitor mode instead (tutorial 31)
Changing a Block policy to allow to stop it blockingCreates an explicit allow that bypasses other Block policiesUse monitor mode — it records matches without allowing anything through

Next Steps