validate

v0.1.4 tool

Validates evidence packs against compliance profiles

Install

epack install tool validate

Adds to epack.yaml, resolves dependencies, downloads binary.

Usage

Run the tool on an evidence pack:

epack tool validate --pack ./evidence.pack

Pass tool-specific arguments after --

Configuration

Or add manually to epack.yaml:

tools:
  validate:
    source: https://github.com/locktivity/epack-tool-validate

Then run epack install to lock and sync.

Overview

epack-tool-validate is an epack tool that validates evidence packs against compliance profile YAML files. It evaluates whether the artifacts in a pack satisfy the requirements defined in a profile.

How It Works

The tool performs validation in several stages:

  1. Profile Resolution: Determines which profile to use from tool configuration
  2. Profile Loading: Loads the base profile and any overlay files
  3. Overlay Application: Merges overlays into the base profile (modify, skip, add)
  4. Profile Compilation: Validates and compiles the profile into an executable form
  5. Pack Indexing: Indexes artifacts by their schema for efficient lookup
  6. Validation: Evaluates each requirement against the indexed artifacts

Profile Structure

Profiles are YAML files that define compliance requirements:

id: my-compliance-profile
name: My Compliance Profile
version: "1.0.0"
description: A compliance profile for validation

requirements:
  - id: REQ-001
    name: MFA Required
    control: CC6.2
    category: Access Control
    satisfied_by:
      any_of:
        - type: evidencepack/idp-posture@v1
          metadata_conditions:
            all:
              - path: $.mfa_enabled
                op: eq
                value: true

Requirement Modes

Requirements can use two clause modes:

  • any_of: Requirement passes if ANY clause matches (first match wins)
  • all_of: Requirement passes if ALL clauses match

Conditions

Conditions use JSONPath expressions to evaluate artifact metadata:

Operator Description
eq Equals
neq Not equals
gt Greater than
gte Greater than or equal
lt Less than
lte Less than or equal
exists Value exists (not null)
not_exists Value does not exist (null)

Cardinality for Multi-Value Paths

When using JSONPath expressions that return multiple values (e.g., $.accounts[*].mfa_enabled), you must specify how to evaluate them:

metadata_conditions:
  all:
    - path: $.accounts[*].mfa_enabled
      op: eq
      value: true
      cardinality: all  # ALL values must satisfy the condition
Cardinality Description
(empty) Default. Path must return exactly one value
all Every value must satisfy the condition
any At least one value must satisfy the condition
none No value should satisfy the condition

Graduated Severity

Clauses can specify a severity level for graded compliance:

satisfied_by:
  any_of:
    # No severity = full pass
    - type: evidencepack/idp-posture@v1
      metadata_conditions:
        all:
          - path: $.mfa_coverage
            op: gte
            value: 100

    # Severity = graded failure
    - type: evidencepack/idp-posture@v1
      severity: low
      metadata_conditions:
        all:
          - path: $.mfa_coverage
            op: gte
            value: 90

Freshness Checks

Clauses can require artifacts to be recent:

- type: evidencepack/vuln-scan@v1
  freshness:
    max_age_days: 30
  metadata_conditions:
    all:
      - path: $.critical_count
        op: eq
        value: 0

Output

The tool outputs a validation.json file containing:

{
  "status": "pass",
  "profile": {
    "id": "my-compliance-profile",
    "name": "My Compliance Profile",
    "version": "1.0.0",
    "digest": "sha256:abc123..."
  },
  "validated_at": "2024-06-15T12:00:00Z",
  "validated_at_label": "just now",
  "pack_digest": "sha256:def456...",
  "summary": {
    "total": 5,
    "passed": 5,
    "failed": 0,
    "missing": 0,
    "warnings": 0
  },
  "requirements": [
    {
      "id": "REQ-001",
      "name": "MFA Required",
      "control": "CC6.2",
      "category": "Access Control",
      "status": "pass",
      "artifact": "artifacts/idp-posture.json",
      "path": "$.enabled",
      "expected": {"op": "eq", "value": true},
      "actual": true,
      "checks": [
        {
          "clause_index": 0,
          "schema": "evidencepack/idp-posture@v1",
          "status": "pass",
          "artifact": "artifacts/idp-posture.json",
          "conditions": [
            {
              "path": "$.enabled",
              "expected": {"op": "eq", "value": true},
              "actual": true,
              "passed": true
            }
          ]
        }
      ]
    }
  ],
  "by_category": {
    "Access Control": {"passed": 3, "failed": 0, "missing": 0},
    "System Operations": {"passed": 2, "failed": 0, "missing": 0}
  }
}

Overlays

Overlays allow customizing profiles without modifying the original:

id: my-overlay
name: Production Overlay

# Modify existing requirements
modify:
  - id: REQ-001
    name: Stricter MFA Requirement
    satisfied_by:
      any_of:
        - type: evidencepack/idp-posture@v1
          metadata_conditions:
            all:
              - path: $.mfa_coverage
                op: gte
                value: 100

# Skip requirements
skip:
  - REQ-003
  - REQ-004

# Add new requirements
add:
  - id: REQ-NEW
    name: New Requirement
    satisfied_by:
      any_of:
        - type: evidencepack/custom@v1

Overlays are applied in order, with last-write-wins semantics.

Limitations

  • Single Profile (MVP): Currently supports one profile per validation run
  • Local Profiles Only: Profiles must be local files (no remote fetch)

Configuration

epack-tool-validate requires a compliance profile to validate against. The profile path is specified in the tool configuration.

Tool Requirements

Requirement Value
Requires Pack Yes
Network Access No

Configuration Options

Option Type Required Description
profile string Yes* Path to a single profile YAML file
profiles []string Yes* List of profile paths (MVP: only one supported)
overlays []string No List of overlay YAML files to apply

*Either profile or profiles must be specified. If both are provided, profiles takes precedence.

Usage

Via epack Build

Add to your build configuration:

# epack.yaml
tools:
  validate:
    source: locktivity/epack-tool-validate@v1
    config:
      profile: profiles/security-policy.yaml

With Overlays

Apply environment-specific customizations:

# epack.yaml
tools:
  validate:
    source: locktivity/epack-tool-validate@v1
    config:
      profile: profiles/security-policy.yaml
      overlays:
        - profiles/overlays/production.yaml
        - profiles/overlays/us-region.yaml

Profile File Format

Profiles are YAML files with the following structure:

id: my-security-profile           # Unique identifier
name: My Security Profile         # Human-readable name
version: "1.0.0"                  # Semantic version
description: Example profile      # Optional description

requirements:
  - id: REQ-001                   # Unique requirement ID
    name: Access Control Policy   # Requirement name
    control: CC6.1                # Optional control reference
    category: Access Control      # Optional category for grouping
    satisfied_by:
      any_of:                     # OR: any clause can satisfy
        - type: evidencepack/idp-posture@v1
          metadata_conditions:
            all:                  # AND: all conditions must match
              - path: $.has_access_policy
                op: eq
                value: true

Clause Options

Field Type Description
type string Required. Artifact schema to match (e.g., evidencepack/idp-posture@v1)
severity string Optional. Failure severity: critical, high, medium, low
freshness.max_age_days int Optional. Maximum age of artifact in days
metadata_conditions.all []Condition Optional. Conditions that must all be true

Condition Options

Field Type Description
path string JSONPath expression (e.g., $.mfa_coverage)
op string Operator: eq, neq, gt, gte, lt, lte, exists, not_exists
value any Expected value (not required for exists/not_exists)
cardinality string For multi-value paths: all, any, none (empty = single value required)

Supported JSONPath Syntax

The following JSONPath patterns are supported:

Pattern Example Description
Root field $.field Access top-level field
Nested field $.nested.field Access nested field
Array index $.array[0] Access specific array element
Last element $.array[-1] Access last array element
Wildcard $.array[*].field Access field in all array elements (requires cardinality)
Recursive descent $..field Access field at any depth (requires cardinality)

Note: Multi-value patterns (wildcards, recursive descent) require a cardinality field to specify how multiple results are evaluated. See Cardinality.

Cardinality for Multi-Value Paths

When a JSONPath expression can return multiple values, you must specify how to evaluate them:

metadata_conditions:
  all:
    - path: $.accounts[*].mfa_enabled
      op: eq
      value: true
      cardinality: all  # ALL accounts must have MFA enabled
Cardinality Description
(empty) Default. Path must return exactly one value
all Every value must satisfy the condition
any At least one value must satisfy the condition
none No value should satisfy the condition

Examples:

# All accounts must have MFA enabled
- path: $.accounts[*].mfa_enabled
  op: eq
  value: true
  cardinality: all

# At least one environment must be production
- path: $.deployments[*].environment
  op: eq
  value: production
  cardinality: any

# No user should have admin role
- path: $.users[*].role
  op: eq
  value: admin
  cardinality: none

Partial presence detection: For cardinality: all, the validator checks that ALL array elements have the field. If some elements are missing the field entirely, validation fails. For example, $.accounts[*].mfa_enabled with cardinality: all will fail if 2 out of 3 accounts have mfa_enabled: true but the third account has no mfa_enabled field - the result will show "2 of 3 elements have field".

Note: Partial presence detection requires a simple wildcard path (e.g., $.accounts[*].field). Recursive descent paths ($..field) cannot detect partial presence since there's no single base array to count.

Overlay File Format

Overlays customize profiles without modifying the original:

id: production-overlay            # Optional identifier
name: Production Overlay          # Optional name

# Modify existing requirements (replaces entire requirement)
modify:
  - id: REQ-001
    name: Stricter Access Control
    satisfied_by:
      any_of:
        - type: evidencepack/idp-posture@v1
          metadata_conditions:
            all:
              - path: $.has_access_policy
                op: eq
                value: true
              - path: $.policy_reviewed_days
                op: lte
                value: 90

# Skip requirements entirely
skip:
  - REQ-003
  - REQ-004

# Add new requirements
add:
  - id: REQ-PROD-001
    name: Production-Specific Check
    category: Production
    satisfied_by:
      any_of:
        - type: evidencepack/deploy-info@v1
          metadata_conditions:
            all:
              - path: $.environment
                op: eq
                value: production

Overlay Application Order

  1. Overlays are applied in the order specified
  2. modify replaces the entire requirement (last-write-wins)
  3. skip removes requirements from evaluation
  4. add appends new requirements (ID must be unique)

Configuration Examples

Basic Single Profile

tools:
  validate:
    source: locktivity/epack-tool-validate@v1
    config:
      profile: profiles/compliance.yaml

Profile with Multiple Overlays

tools:
  validate:
    source: locktivity/epack-tool-validate@v1
    config:
      profile: profiles/security-policy.yaml
      overlays:
        - profiles/overlays/skip-manual.yaml
        - profiles/overlays/environment-prod.yaml

Environment-Specific Configuration

Use environment variables in overlay paths:

tools:
  validate:
    source: locktivity/epack-tool-validate@v1
    config:
      profile: profiles/security-baseline.yaml
      overlays:
        - profiles/overlays/${ENVIRONMENT}.yaml

Shared Configuration with YAML Anchors

Use YAML anchors to share configuration between multiple tool instances:

tools:
  validate:
    source: locktivity/epack-tool-validate@v1
    config: &base-validate
      profile: profiles/security-baseline.yaml

  validate-strict:
    source: locktivity/epack-tool-validate@v1
    config:
      <<: *base-validate
      overlays:
        - profiles/overlays/strict.yaml

Output Files

File Description
validation.json JSON file containing validation results

Exit Codes

Exit Code Meaning
0 Validation completed (check validation.json for pass/fail)
1 Operational error (profile not found, invalid YAML, etc.)

Note: A failing validation (requirements not met) still exits with code 0. Check the status field in validation.json to determine pass/fail.

Examples

Basic Usage

Run validation on an evidence pack:

epack tool validate --pack ./my-evidence.epack

Interpreting Results

Passing Validation

{
  "status": "pass",
  "profile": {
    "id": "security-baseline",
    "name": "Security Baseline",
    "version": "1.0.0"
  },
  "validated_at": "2024-06-15T12:00:00Z",
  "pack_digest": "sha256:abc123...",
  "summary": {
    "total": 5,
    "passed": 5,
    "failed": 0,
    "warnings": 0
  },
  "requirements": [
    {
      "id": "REQ-001",
      "name": "MFA Enabled",
      "status": "pass",
      "artifact": "artifacts/idp-posture.json"
    }
  ]
}

All requirements are satisfied.

Failing Validation

{
  "status": "fail",
  "profile": {
    "id": "security-baseline",
    "name": "Security Baseline",
    "version": "1.0.0"
  },
  "summary": {
    "total": 5,
    "passed": 3,
    "failed": 1,
    "missing": 1,
    "warnings": 0
  },
  "requirements": [
    {
      "id": "REQ-001",
      "name": "MFA Coverage",
      "status": "fail",
      "severity": "high",
      "expected": {
        "op": "gte",
        "value": 100
      },
      "actual": 85,
      "delta": -15,
      "message": "conditions not satisfied",
      "artifact": "artifacts/idp-posture.json",
      "path": "$.mfa_coverage",
      "failure_kind": "condition",
      "checks": [
        {
          "clause_index": 0,
          "schema": "evidencepack/idp-posture@v1",
          "status": "fail",
          "artifact": "artifacts/idp-posture.json",
          "message": "conditions not satisfied",
          "conditions": [
            {
              "path": "$.mfa_coverage",
              "expected": {"op": "gte", "value": 100},
              "actual": 85,
              "delta": -15,
              "passed": false
            }
          ]
        }
      ]
    },
    {
      "id": "REQ-002",
      "name": "Vulnerability Scan",
      "status": "fail",
      "severity": "high",
      "failure_kind": "missing",
      "message": "no artifact with schema evidencepack/vuln-scan@v1",
      "checks": [
        {
          "clause_index": 0,
          "schema": "evidencepack/vuln-scan@v1",
          "status": "missing",
          "message": "no artifact with schema evidencepack/vuln-scan@v1"
        }
      ]
    }
  ],
  "key_failures": [
    {"id": "REQ-001", "name": "MFA Coverage", "severity": "high"},
    {"id": "REQ-002", "name": "Vulnerability Scan", "severity": "high"}
  ]
}

The key_failures array highlights critical and high severity failures.

Output fields for failed requirements:

Field Description
expected The condition that needed to be satisfied (op and value)
actual The actual value found in the artifact
delta Numeric difference from expected (for numeric comparisons)
path JSONPath that was evaluated
artifact Path to the artifact that was checked
failure_kind Structured failure classification such as missing or condition
checks Per-clause evaluation details, including missing or passing checks

Example Profiles

Basic Access Control Profile

id: access-control-basic
name: Basic Access Control Profile
version: "1.0.0"

requirements:
  - id: AC-001
    name: MFA Enabled
    control: AC-1
    category: Access Control
    satisfied_by:
      any_of:
        - type: evidencepack/idp-posture@v1
          metadata_conditions:
            all:
              - path: $.mfa_enabled
                op: eq
                value: true

  - id: AC-002
    name: Password Policy
    control: AC-2
    category: Access Control
    satisfied_by:
      any_of:
        - type: evidencepack/idp-posture@v1
          metadata_conditions:
            all:
              - path: $.password_policy.min_length
                op: gte
                value: 12
              - path: $.password_policy.require_special
                op: eq
                value: true

Graduated Compliance Profile

This profile uses graduated severity for MFA coverage thresholds:

id: mfa-graduated
name: MFA Coverage with Graduated Compliance
version: "1.0.0"

requirements:
  - id: MFA-001
    name: MFA Coverage
    control: CC6.2
    category: Access Control
    satisfied_by:
      any_of:
        # Full compliance (100%) - no severity = PASS
        - type: evidencepack/idp-posture@v1
          metadata_conditions:
            all:
              - path: $.mfa_coverage
                op: gte
                value: 100

        # 90%+ coverage - low severity
        - type: evidencepack/idp-posture@v1
          severity: low
          metadata_conditions:
            all:
              - path: $.mfa_coverage
                op: gte
                value: 90

        # 75%+ coverage - medium severity
        - type: evidencepack/idp-posture@v1
          severity: medium
          metadata_conditions:
            all:
              - path: $.mfa_coverage
                op: gte
                value: 75

        # Anything less - high severity
        - type: evidencepack/idp-posture@v1
          severity: high
          metadata_conditions:
            all:
              - path: $.mfa_coverage
                op: gte
                value: 0
MFA Coverage Result Severity
100% Pass -
95% Fail low
80% Fail medium
50% Fail high

Profile with Freshness Requirements

id: vuln-scan-fresh
name: Vulnerability Scanning Profile
version: "1.0.0"

requirements:
  - id: VS-001
    name: Recent Vulnerability Scan
    control: CC7.1
    category: System Operations
    satisfied_by:
      any_of:
        - type: evidencepack/vuln-scan@v1
          freshness:
            max_age_days: 7
          metadata_conditions:
            all:
              - path: $.critical_count
                op: eq
                value: 0

  - id: VS-002
    name: No High Vulnerabilities
    control: CC7.2
    category: System Operations
    satisfied_by:
      any_of:
        - type: evidencepack/vuln-scan@v1
          freshness:
            max_age_days: 30
          metadata_conditions:
            all:
              - path: $.high_count
                op: lte
                value: 5

All-Of Mode Example

This requirement needs ALL clauses to be satisfied:

requirements:
  - id: DEPLOY-001
    name: Production Deployment Checks
    category: Deployment
    satisfied_by:
      all_of:
        - type: evidencepack/deploy-info@v1
          metadata_conditions:
            all:
              - path: $.environment
                op: eq
                value: production

        - type: evidencepack/approval@v1
          metadata_conditions:
            all:
              - path: $.approved
                op: eq
                value: true

        - type: evidencepack/test-results@v1
          metadata_conditions:
            all:
              - path: $.all_passed
                op: eq
                value: true

Multi-Value JSONPath with Cardinality

Use cardinality when checking conditions across arrays:

requirements:
  - id: CLOUD-001
    name: Root MFA Enabled (All Accounts)
    category: Identity
    satisfied_by:
      any_of:
        - type: evidencepack/cloud-posture@v1
          metadata_conditions:
            all:
              # Check ALL accounts have root MFA enabled
              - path: $.accounts[*].iam.root_mfa_enabled
                op: eq
                value: true
                cardinality: all

  - id: CLOUD-002
    name: At Least One High-Availability Region
    category: Infrastructure
    satisfied_by:
      any_of:
        - type: evidencepack/cloud-posture@v1
          metadata_conditions:
            all:
              # At least one region has HA enabled
              - path: $.regions[*].high_availability
                op: eq
                value: true
                cardinality: any

  - id: CLOUD-003
    name: No Public S3 Buckets
    category: Data Protection
    satisfied_by:
      any_of:
        - type: evidencepack/cloud-posture@v1
          metadata_conditions:
            all:
              # NO bucket should be public
              - path: $.s3_buckets[*].public_access
                op: eq
                value: true
                cardinality: none
Cardinality Use Case
all Every item must satisfy (e.g., all accounts have MFA)
any At least one item must satisfy (e.g., one region has HA)
none No item should satisfy (e.g., no public buckets)

Example Overlays

Skip Manual Requirements

id: skip-manual
name: Skip Manual Review Requirements

skip:
  - MANUAL-001
  - MANUAL-002
  - MANUAL-003

Environment-Specific Overlay

id: production-overlay
name: Production Environment Overlay

modify:
  - id: VS-001
    name: Strict Vulnerability Scan (Production)
    satisfied_by:
      any_of:
        - type: evidencepack/vuln-scan@v1
          freshness:
            max_age_days: 1  # Daily scans in production
          metadata_conditions:
            all:
              - path: $.critical_count
                op: eq
                value: 0
              - path: $.high_count
                op: eq
                value: 0

add:
  - id: PROD-001
    name: Production Monitoring Active
    category: Production
    satisfied_by:
      any_of:
        - type: evidencepack/monitoring@v1
          metadata_conditions:
            all:
              - path: $.active
                op: eq
                value: true

Integration with CI/CD

GitHub Actions Example

name: Compliance Check

on:
  push:
    branches: [main]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install epack
        run: |
          go install github.com/locktivity/epack/cmd/epack@latest

      - name: Build evidence pack
        run: epack collect -o evidence.epack

      - name: Validate compliance
        run: |
          epack tool validate --pack evidence.epack

          # Check validation result
          STATUS=$(jq -r '.status' validation.json)
          if [ "$STATUS" != "pass" ]; then
            echo "Compliance check failed"
            jq '.key_failures' validation.json
            exit 1
          fi

          echo "Compliance check passed"

GitLab CI Example

compliance:
  stage: test
  script:
    - epack collect -o evidence.epack
    - epack tool validate --pack evidence.epack
    - |
      if [ "$(jq -r '.status' validation.json)" != "pass" ]; then
        echo "Compliance failures:"
        jq '.requirements[] | select(.status == "fail")' validation.json
        exit 1
      fi
  artifacts:
    paths:
      - validation.json
    when: always

Debugging Validation Failures

Check Artifact Schema

Ensure your artifacts have the expected schema:

# List artifact schemas in pack
epack inspect --pack evidence.epack | jq '.artifacts[].schema'

Verify JSONPath

Test your JSONPath expressions against artifact content:

# Extract artifact and test JSONPath
unzip -p evidence.epack artifacts/idp-posture.json | jq '$.mfa_coverage'

Profile Compilation Errors

If profile compilation fails, check:

  1. All requirement IDs are unique
  2. Each requirement has either any_of or all_of (not both)
  3. Each clause has a type field
  4. Severity values are valid: critical, high, medium, low
  5. Multi-value JSONPath expressions (wildcards, ..) have a cardinality field
  6. Cardinality values are valid: all, any, none
v0.1.4 Latest
2026-04-08

**Full Changelog**: https://github.com/locktivity/epack-tool-validate/compare/v0.1.3...v0.1.4

darwin/amd64 darwin/arm64 linux/amd64 linux/arm64
v0.1.3
2026-04-08

**Full Changelog**: https://github.com/locktivity/epack-tool-validate/compare/v0.1.2...v0.1.3

darwin/amd64 darwin/arm64 linux/amd64 linux/arm64
v0.1.2
2026-03-19

**Full Changelog**: https://github.com/locktivity/epack-tool-validate/compare/v0.1.1...v0.1.2

darwin/amd64 darwin/arm64 linux/amd64 linux/arm64

Details

Publisher
locktivity
Latest
v0.1.4
Protocol
v1

Platforms

darwin/amd64 darwin/arm64 linux/amd64 linux/arm64

Links