Skip to main content

Outputs

Each policy can have multiple outputs. Outputs control the format, destination, and presentation of results.

Output Configuration

{
"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": "Review required before merging",
"display_ignores": true
}

Output Configuration Options

FieldRequiredDescription
formatYesOutput format: markdown, html, or json
destinationYesWhere to send output (see Destinations)
templateNoTemplate style: table or text (ignored for JSON)
fieldsNoWhich fields to include in output
changesNoFilter to specific change categories: new, changed, removed, existing
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
output_reasonNoCustom failure message (overrides technical reason)
display_ignoresNoShow ignored items in output (default: true)

Formats

FormatDescriptionTemplate Required
markdownGitHub-flavored markdown tables or textYes: table or text
htmlHTML formatted outputYes: table or text
jsonStructured JSON arrayNo (template must be empty)

Markdown/HTML Templates

  • table (default): Compact table layout, one row per finding
  • text: Narrative format with severity icons and structured sections

JSON Output

JSON outputs are always arrays. Each policy produces an array of finding objects:

[
{
"policy": "block-critical",
"title": "Vulnerability Policy: block-critical",
"fields": ["VulnerabilityID", "PkgName", "Severity", "FixedVersion"],
"sources": {
"vulnerabilities": {
"new": {
"action": "block",
"change": "new",
"header": "Detection: new - Action: block",
"items": [
{
"VulnerabilityID": "CVE-2024-0001",
"PkgName": "lodash",
"Severity": "CRITICAL",
"FixedVersion": "4.17.21"
}
]
}
}
}
}
]

Important: When using format: json, do not set template. It will cause a validation error.


Destinations

DestinationDescription
git:prPost as PR comment (requires PR context)
git:issueCreate/update GitHub issue
file:/pathWrite to file (absolute or relative)
log:stdoutPrint to standard output
log:stderrPrint to standard error
url:https://...HTTP POST to webhook endpoint

git:pr

Posts or updates a comment on the pull request. Requires:

  • CODEWARD_MODE=diff
  • CODEWARD_GITHUB_TOKEN, CODEWARD_GITHUB_OWNER, CODEWARD_GITHUB_REPOSITORY, CODEWARD_GITHUB_PR_NUMBER
{ "format": "markdown", "destination": "git:pr" }

git:issue

Creates or updates a GitHub issue. Typically used for tracking existing backlog. Requires:

  • CODEWARD_MODE=main (default)
  • CODEWARD_GITHUB_TOKEN, CODEWARD_GITHUB_OWNER, CODEWARD_GITHUB_REPOSITORY
{ "format": "markdown", "destination": "git:issue", "changes": ["existing"] }

Note: Issue titles are suppressed (handled by GitHub).

file:

Write to a file. Supports both absolute and relative paths:

  • file:/path/to/file -> Absolute path
  • file:path/to/file -> Relative path (from where scanner is run)
  • file:./path/to/file -> Relative path
{ "format": "json", "destination": "file:vulnerabilities.json" }

log:stdout / log:stderr

Print to console (useful for debugging or simple CI logs):

{ "format": "markdown", "destination": "log:stdout" }

url:

HTTP POST to a webhook. Body is the rendered output. Content-Type is auto-detected (application/json for JSON outputs):

{ "format": "json", "destination": "url:https://hooks.example.com/security" }

The X-Report-Title header is included when a title is available.

Retry Logic: Codeward automatically retries failed webhook requests (network errors, 5xx server errors):

  • Attempts: 3 retries maximum
  • Backoff: 1 second wait between attempts
  • No retry: Client errors (4xx) are not retried

Notes:

  • Non-2xx responses are logged but don't affect exit code
  • Empty results are skipped (no POST sent)
  • Only POST is implemented (idempotency left to receiver)

Filtering by Change Category

Use changes to limit which findings appear:

{ "changes": ["new", "changed"] }
CategoryWhen to Use
newPR comments — show introduced issues
changedWhen attributes shifted (version, severity)
removedTrack remediation progress
existingBacklog tracking (issues, JSON exports)

Common pattern:

  • PR comment: ["new", "changed"] — actionable delta
  • Issue: ["existing"] — backlog tracking
  • JSON export: all categories for full audit

Field Selection

Limit which fields appear in output:

{ "fields": ["VulnerabilityID", "PkgName", "Severity", "FixedVersion"] }

If omitted, a default set of fields is included. See Policies for allowed fields per policy type.


Grouping

Group findings by field values:

{ "group_by": ["Severity"] }

Creates sections for each severity level. Multiple fields create nested grouping.

Severity aggregation: When grouping omits Severity, the highest severity in each group is shown in the header.

Note: Grouping doesn't affect JSON output (remains flat array). Process JSON client-side if needed.


Combining Outputs

Multiple policies can write to the same destination. Use combined: true to merge them:

{
"vulnerability": [{
"name": "critical",
"outputs": [{
"format": "markdown",
"destination": "git:pr",
"combined": true
}]
}],
"license": [{
"name": "gpl",
"outputs": [{
"format": "markdown",
"destination": "git:pr",
"combined": true
}]
}]
}

This produces a single PR comment with both vulnerability and license sections.

Combining Rules

MixAllowed
Markdown + Markdown✅ Merged into one document
HTML + HTML✅ Merged into one document
JSON + JSON✅ Single concatenated array
Markdown + HTML✅ Treated as separate groups
JSON + MarkdownError: formats must match

JSON combining: Produces a single array containing all policies' results. No wrapper object.

Empty Combined Outputs

If all policies in a combined group produce zero results (after filtering), the entire output is suppressed.


Collapsible Sections

For long reports, use collapse: true:

{ "collapse": true }

Renders markdown/HTML sections as collapsible (using <details> tags). Ignored for JSON.


Title and Comment

Customize the section header:

{
"title": "Critical Vulnerabilities",
"comment": "These must be fixed before merging"
}
  • title: Section heading
  • comment: Explanatory text below the title

Examples

PR Comment with New Issues Only

{
"format": "markdown",
"destination": "git:pr",
"template": "table",
"fields": ["VulnerabilityID", "PkgName", "Severity", "FixedVersion"],
"changes": ["new"],
"collapse": true,
"title": "New Vulnerabilities"
}

JSON Export for Automation

{
"format": "json",
"destination": "file:/results/security-report.json",
"changes": ["new", "changed", "removed"],
"combined": true
}

Backlog Issue

{
"format": "markdown",
"destination": "git:issue",
"template": "table",
"fields": ["VulnerabilityID", "PkgName", "Severity"],
"changes": ["existing"],
"title": "Security Backlog"
}

Webhook Notification

{
"format": "json",
"destination": "url:https://hooks.slack.com/services/...",
"changes": ["new"],
"title": "New Security Issues"
}

Combined PR Comment (Multiple Policies)

{
"vulnerability": [{
"name": "vulns",
"outputs": [{
"format": "markdown",
"destination": "git:pr",
"combined": true,
"title": "Vulnerabilities"
}]
}],
"license": [{
"name": "licenses",
"outputs": [{
"format": "markdown",
"destination": "git:pr",
"combined": true,
"title": "License Issues"
}]
}]
}

Common Mistakes

ProblemCauseFix
Empty PR commentOnly existing in changesInclude new or changed
Template error with JSONSet template for JSON formatRemove template field
Mixed format errorcombined: true with JSON + markdownUse same format for all combined outputs
File not found (Docker)Path not in a mounted volumeWrite to a mounted path (e.g. file:/results/out.json)
No PR commentMissing GitHub env varsSet CODEWARD_GITHUB_TOKEN, CODEWARD_GITHUB_PR_NUMBER, etc.
Invalid field errorField not allowed for policy typeCheck allowed fields in Policies

Customizing Failure Messages

By default, Codeward outputs the technical reason for a policy violation (e.g., "Field 'scripts.test' missing"). You can override this with a user-friendly message using output_reason.

This is especially useful for Validation policies:

{
"rules": [
{
"key": "scripts.test",
"type": "not_exists",
"output_reason": "Missing required test script. Run 'npm pkg set scripts.test=\"vitest\"' to fix."
}
]
}

The output will use this text instead of the default error message.