Skip to main content
Version: Latest

Configuration

Codeward is configured with a single file in your repository root. Both JSON and YAML formats are fully supported.

Ready-Made Profiles

Don't want to write configs from scratch? Browse the Codeward Registry for ready-to-use policy profiles covering security, infrastructure, language-specific checks, compliance, and more.

Config File Discovery

Codeward looks for configuration in this order (first found wins):

  1. Path specified by CODEWARD_CONFIG_PATH (or --config CLI flag)
  2. .codeward.json
  3. .codeward.yaml
  4. .codeward.yml
  5. Built-in defaults (if no config found)

Top-Level Structure

# .codeward.yaml
global:
dependency_tree: false
vulnerability: # vulnerability policies
license: # license policies
package: # package policies
file: # file validation policies
pr: # PR validation policies
sbom: # SBOM export settings (optional)

All policy arrays are optional. Omit any you don't need.

Global Settings

SettingTypeDefaultDescription
dependency_treebooleanfalseEnable dependency graph analysis. Adds Relationship, Parents, Children, Sources fields.
modestring-Execution mode: diff or main. See Execution Mode.
loggingobjectSee belowLogging configuration.
ignorearray[]Global ignore rules. See Ignore Rules.

Execution Mode

The CODEWARD_MODE environment variable (or global.mode in config) controls how Codeward scans.

ModeWhen to UseBehavior
diffPull requestsCompares feature vs main branch. Categorizes as new, changed, removed, existing. Enables git:pr.
mainPush to main, scheduled scansScans main branch only. All findings are existing. Enables git:issue.

Key insight: Only new findings should typically block PRs. Existing technical debt doesn't slow you down.

Setting the Mode

# Via environment variable (recommended for CI)
export CODEWARD_MODE=diff

# Via CLI flag
codeward-scan --mode diff
# Via config file
global:
mode: diff

CLI flags > environment variables > config file.

Policy Structure

Every policy follows this structure:

name: policy-name
disabled: false
actions:
new: block
existing: warn
removed: info
changed: warn
operator: or # or, and, implies
rules:
- { field: Severity, type: eq, value: CRITICAL }
outputs:
- format: markdown
destination: git:pr
FieldRequiredDescription
nameYesUnique identifier for the policy
disabledNoSet true to skip this policy
actionsYesWhat to do for each change category
operatorNoor (default), and, or implies for rule matching
rulesYesArray of filter rules (can be empty for "match all")
outputsYesWhere and how to report results

Policy Validation

Codeward validates your configuration. If a policy has errors:

  1. The invalid policy is skipped with a warning
  2. Valid policies continue executing
  3. Scan only fails if all policies are invalid

This ensures a typo in one policy doesn't break your entire CI pipeline.

Actions

ActionEffectExit Code
infoLog only, no impact on CI0
warnVisible in output, CI passes0
blockCI fails (non-zero exit code)1
ignoreSuppress from output entirely0
File/PR Validation

File and PR policies use only the existing action since they don't track changes over time. Config validation will warn if new, removed, or changed actions are specified.

Change Categories

CategoryMeaningTypical Action
newOnly in feature branch (introduced)block or warn
changedIn both, but attributes differwarn
removedOnly in main branch (deleted)info
existingIn both, unchangedinfo or warn

Rules

Rules define what to search for. When a rule matches, it creates a finding that triggers the configured action.

Rule Types

TypeReports WhenExample
eqValue equals target{ field: Severity, type: eq, value: CRITICAL }
neValue does not equal target{ field: Severity, type: ne, value: LOW }
lt / gtLess / greater than (semver-aware){ field: Score, type: gt, value: "7.0" }
le / geLess/greater or equal (semver-aware){ key: changed_files, type: ge, value: "20" }
containsValue contains substring{ field: Title, type: contains, value: injection }
not_containsValue does not contain substring{ field: PkgName, type: not_contains, value: test }
hasPrefixValue starts with string{ field: VulnerabilityID, type: hasPrefix, value: CVE-2024 }
hasSuffixValue ends with string{ field: PkgName, type: hasSuffix, value: -dev }
regexValue matches regex{ field: Description, type: regex, value: "remote.*code" }
not_regexValue does not match regex{ key: title, type: not_regex, value: "^(feat|fix|docs):" }
inValue is in comma-separated set{ field: Severity, type: in, value: "CRITICAL,HIGH" }
not_inValue is not in set{ field: Category, type: not_in, value: "test,mock" }
existsKey or file exists{ type: exists, key: scripts.test }
not_existsKey or file does not exist{ type: not_exists, path: ".env.local" }
last_matchLast line matching filter matches valueSee Last Match Rules
not_last_matchLast line matching filter does NOT match valueSee Last Match Rules
RE2 Regex

Go's regexp uses RE2 which does not support lookahead/lookbehind. Use not_regex instead of (?!...). See RE2 Reference.

Rule Field Naming

Policy TypePropertyDescription
vulnerability / license / packagefieldEntity field name (e.g., Severity, PkgName)
file / prkey (or field alias)Key path (e.g., version, dependencies.axios)

Field names are case-insensitive for PR policies.

Policy Operators

OR (default) — match if any rule matches:

operator: or
rules:
- { field: Severity, type: eq, value: CRITICAL }
- { field: Severity, type: eq, value: HIGH }

AND — match only if all rules match:

operator: and
rules:
- { field: Severity, type: eq, value: CRITICAL }
- { field: PkgName, type: eq, value: lodash }

implies — conditional "If X then Y" logic:

operator: implies
rules:
- { key: changed_files, type: gt, value: "10" } # trigger
- { key: "files.filename", file_pattern: "docs/**", type: not_exists } # condition
  • First rule is the trigger — if it doesn't match, policy passes silently
  • Remaining rules are conditions — evaluated only if trigger matches
  • Use RuleRole field in outputs to show trigger/condition labels

Output Configuration

outputs:
- format: markdown
destination: git:pr
template: table
fields: [VulnerabilityID, PkgName, Severity, FixedVersion]
changes: [new, changed]
group_by: [Severity]
combined: false
collapse: true
title: Security Issues
comment: Please fix before merging
FieldRequiredDescription
formatYesmarkdown, html, json, or sarif
destinationYesWhere to send output (see Outputs)
templateNotable, text, or combined (not for JSON)
fieldsNoWhich fields to include (defaults per policy type)
changesNoFilter to change categories
group_byNoGroup results by these fields
combinedNoMerge with other outputs at same destination
collapseNoMake section collapsible (markdown/html)
titleNoCustom section title
commentNoText shown below title
webhookNoCustom webhook config (only for url: destinations)

See Outputs for full details.

Ignore Rules

Global Ignores

Filter findings based on file paths and targets across all policies:

global:
ignore:
- name: Ignore test directories
paths: ["test/**", "**/test/**", "**/*_test.go"]
- name: Ignore specific CVEs
targets: [CVE-2023-1234, CVE-2023-5678]
- name: Ignore vendor CVEs (with expiry)
paths: ["vendor/**"]
targets: [CVE-2024-0001]
expires: "2026-06-30"
FieldDescription
nameName for logging (required)
pathsGlob patterns for file paths
targetsItems to ignore (CVE IDs, package names, etc.)
expiresExpiration date YYYY-MM-DD (rule ignored after this date)

If both paths and targets are specified, finding must match both to be ignored.

Policy-Level Ignores

Content-based ignores within a specific policy:

vulnerability:
- name: Critical vulnerabilities
actions: { new: block }
rules:
- { field: Severity, type: eq, value: CRITICAL }
ignores:
- name: Windows-only CVE
expires: "2026-06-30"
author: security-[email protected]
operator: and
rules:
- { field: VulnerabilityID, type: eq, value: CVE-2024-0001 }
- { field: PkgName, type: eq, value: openssl }
FieldDescription
operatoror (default) or and
expiresExpiration date YYYY-MM-DD
rulesFilter rules using same syntax as policy rules

Logging

global:
logging:
level: info # silent, error, warn, info, debug, trace
format: text # text or json
show_timestamp: false
summary: standard # none, minimal, standard, detailed

Environment variables override config: CODEWARD_LOG_LEVEL, CODEWARD_LOG_FORMAT, CODEWARD_LOG_TIMESTAMP, CODEWARD_LOG_SUMMARY.

Default Configuration

When no config file exists, Codeward uses these defaults:

  • Vulnerabilities: Warns on new/existing CRITICAL and HIGH. Posts to PR + creates issues.
  • Licenses: Warns on new/existing Forbidden, Restricted categories and UNKNOWN severity. Posts to PR + creates issues.
  • Packages: Info for all changes. Posts to PR.
  • File/PR Validation: Not enabled by default.

Starter Configurations

Block Critical Only

vulnerability:
- name: block-critical
actions: { new: block }
rules:
- { field: Severity, type: eq, value: CRITICAL }
outputs:
- format: markdown
destination: git:pr
fields: [VulnerabilityID, PkgName, Severity, FixedVersion]
changes: [new]

Full: Vulnerabilities + Licenses + Packages

vulnerability:
- name: crit-high-block
actions: { new: block, existing: warn }
rules:
- { field: Severity, type: eq, value: CRITICAL }
- { field: Severity, type: eq, value: HIGH }
outputs:
- format: markdown
destination: git:pr
fields: [VulnerabilityID, PkgName, Severity, FixedVersion]
changes: [new]

license:
- name: gpl-block
actions: { new: block }
rules:
- { field: Category, type: eq, value: Copyleft }
outputs:
- format: markdown
destination: git:pr
fields: [Name, Category, Severity, PkgName]
changes: [new]

package:
- name: new-packages
actions: { new: info }
rules: []
outputs:
- format: markdown
destination: git:pr
fields: [Name, Version]
changes: [new]

Progressive Enforcement

PhaseApproach
1. ObserveAll actions set to info
2. WarnChange new to warn
3. Block CriticalSet new: block for CRITICAL only
4. ExpandAdd HIGH to blocking
5. Steady StateBlock what matters, warn on the rest

Tip: Use separate policies for different severity levels so you can promote them independently.