epack install tool validate
Adds to epack.yaml, resolves dependencies, downloads binary.
Run the tool on an evidence pack:
epack tool validate --pack ./evidence.pack
Pass tool-specific arguments after --
Or add manually to epack.yaml:
tools:
validate:
source: https://github.com/locktivity/epack-tool-validate
Then run epack install to lock and sync.
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.
The tool performs validation in several stages:
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
Requirements can use two clause modes:
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) |
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 |
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
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
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 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.
epack-tool-validate requires a compliance profile to validate against. The profile path is specified in the tool configuration.
| Requirement | Value |
|---|---|
| Requires Pack | Yes |
| Network Access | No |
| 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.
Add to your build configuration:
# epack.yaml
tools:
validate:
source: locktivity/epack-tool-validate@v1
config:
profile: profiles/security-policy.yaml
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
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
| 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 |
| 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) |
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.
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.
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
modify replaces the entire requirement (last-write-wins)skip removes requirements from evaluationadd appends new requirements (ID must be unique)tools:
validate:
source: locktivity/epack-tool-validate@v1
config:
profile: profiles/compliance.yaml
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
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
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
| File | Description |
|---|---|
validation.json |
JSON file containing validation results |
| 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.
Run validation on an evidence pack:
epack tool validate --pack ./my-evidence.epack
{
"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.
{
"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 |
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
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 |
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
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
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) |
id: skip-manual
name: Skip Manual Review Requirements
skip:
- MANUAL-001
- MANUAL-002
- MANUAL-003
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
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"
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
Ensure your artifacts have the expected schema:
# List artifact schemas in pack
epack inspect --pack evidence.epack | jq '.artifacts[].schema'
Test your JSONPath expressions against artifact content:
# Extract artifact and test JSONPath
unzip -p evidence.epack artifacts/idp-posture.json | jq '$.mfa_coverage'
If profile compilation fails, check:
any_of or all_of (not both)type fieldcritical, high, medium, low..) have a cardinality fieldall, any, none**Full Changelog**: https://github.com/locktivity/epack-tool-validate/compare/v0.1.3...v0.1.4
**Full Changelog**: https://github.com/locktivity/epack-tool-validate/compare/v0.1.2...v0.1.3
**Full Changelog**: https://github.com/locktivity/epack-tool-validate/compare/v0.1.1...v0.1.2