Policy Evaluation
Built-in policy validation rules and compliance checking in evidence collection.
Overview
Policy evaluation happens at three stages:
- Pre-collection - Configuration and credential validation
- During collection - API scope and permission enforcement
- Post-collection - Bundle content and size validation
Goals:
- Prevent collection of sensitive data
- Enforce read-only access
- Validate compliance requirements
- Ensure bundle integrity
Pre-Collection Validation
Configuration Schema Validation
Checks before collection starts:
✅ Valid configuration:
framework: soc2_type1
controls:
- CC6.1
sources:
github:
mode: token
token_env: GITHUB_TOKEN
org: acme
repos:
- acme/backend
bundle:
signing:
private_key_path: ~/.evidence/keys/private.pem❌ Invalid configuration:
framework: invalid_framework # Not recognized
controls: [] # At least one control required
sources:
github:
# Missing required fieldsValidation errors:
Configuration validation failed:
- framework: must be one of [soc2_type1, soc2_type2]
- controls: must contain at least one control
- sources.github.token_env: required field missing
- sources.github.org: required field missing
- sources.github.repos: required field missingCredential Validation
Before attempting collection:
GitHub token:
Validating GitHub credentials...
→ Testing authentication
→ Checking token scopes
✓ Token valid
✓ Scopes: repo:read, read:orgAWS credentials:
Validating AWS credentials...
→ Testing STS GetCallerIdentity
→ Checking IAM permissions
✓ Credentials valid
✓ Account: 123456789012Google Workspace:
Validating Google Workspace credentials...
→ Testing service account
→ Checking domain-wide delegation
✓ Service account valid
✓ Scopes authorizedValidation failures:
✗ Validation failed
Error: GitHub token has insufficient scopes
Required: repo:read, read:org
Actual: public_repo
Update token scopes and try again.Signing Key Validation
Before signing bundle:
Validating signing key...
→ Checking file exists
→ Verifying key format
→ Testing key permissions
✓ Private key valid (Ed25519)
✓ File permissions: 600 (secure)Validation failures:
✗ Signing key validation failed
Error: Private key file has insecure permissions (644)
Expected: 600 (owner read/write only)
Fix with: chmod 600 ~/.evidence/keys/private.pemDuring Collection Validation
API Scope Enforcement
GitHub token scopes:
✅ Allowed scopes:
repo:read- Read repository datapublic_repo- Read public repository dataread:org- Read organization data
❌ Forbidden scopes:
repo(write access)admin:org(admin access)delete_repo(destructive)write:org(write access)
Enforcement:
// SDK checks token scopes before use
const scopes = await getTokenScopes(token);
const forbidden = ['repo', 'admin:org', 'delete_repo', 'write:org'];
const hasForbidden = scopes.some(scope => forbidden.includes(scope));
if (hasForbidden) {
throw new Error('Token has forbidden write scopes');
}Permission Verification
AWS IAM permissions:
✅ Allowed actions:
{
"Effect": "Allow",
"Action": [
"iam:Get*",
"iam:List*",
"cloudtrail:Describe*",
"cloudtrail:Get*",
"logs:Describe*"
]
}❌ Forbidden actions:
{
"Effect": "Deny",
"Action": [
"iam:Create*",
"iam:Delete*",
"iam:Update*",
"iam:Put*",
"cloudtrail:Create*",
"cloudtrail:Delete*",
"logs:Create*",
"logs:Delete*"
]
}Enforcement:
- SDK uses only read-only API calls
- If write permission detected, warning issued
- Collection continues with read-only operations
Rate Limit Handling
GitHub API rate limiting:
Policy:
- Check rate limit before each request
- If remaining < 100, pause collection
- Wait until rate limit resets
- Resume collection
Behavior:
Collecting from GitHub...
→ Checking rate limit: 4,856 / 5,000 remaining
→ Fetching organization settings
→ Checking rate limit: 4,855 / 5,000 remaining
→ Fetching repository list
...
⚠ Rate limit approaching (98 remaining)
Pausing collection...
Rate limit resets at 2026-01-09T14:00:00Z (in 5 minutes)
Waiting...
✓ Rate limit reset
Resuming collection...Post-Collection Validation
Forbidden Content Detection
Sensitive data patterns blocked:
❌ Secrets:
- AWS access keys (AKIA...)
- Private keys (-----BEGIN PRIVATE KEY-----)
- OAuth tokens
- API keys
- Passwords
❌ Source code:
- Repository file contents
- Commit diffs
- Pull request diffs
❌ Personal data:
- Email content (Gmail)
- Chat messages (Slack, Teams)
- Calendar events details
Validation:
// SDK scans artifacts for forbidden patterns
const forbiddenPatterns = [
/AKIA[0-9A-Z]{16}/, // AWS access key
/-----BEGIN (PRIVATE|RSA) KEY-----/, // Private keys
/ghp_[a-zA-Z0-9]{36}/, // GitHub token
/sk_live_[a-zA-Z0-9]+/, // Stripe key
];
for (const artifact of artifacts) {
for (const pattern of forbiddenPatterns) {
if (pattern.test(artifact.content)) {
throw new Error(`Forbidden content detected in ${artifact.filename}`);
}
}
}If detected:
✗ Collection failed
Error: Forbidden content detected
File: sources/github/commit_abc123.json
Pattern: AWS access key (AKIA...)
This indicates a configuration error or security issue.
evidence SDK never collects secrets or source code.Bundle Size Validation
Default maximum: 50 MB (configurable)
Check before creating bundle:
Validating bundle size...
→ Calculating total size
→ Total: 35.2 KB
✓ Within limit (50 MB)If exceeds:
✗ Bundle creation failed
Error: Bundle size (75.3 MB) exceeds maximum (50 MB)
Options:
1. Increase limit: bundle.max_size_mb: 100
2. Reduce scope: Limit repos or log groups
3. Disable verbose manifestArtifact Count Validation
Maximum artifacts: 10,000 per bundle
Purpose: Prevent unbounded resource usage
Check:
Validating artifact count...
→ Total artifacts: 127
✓ Within limit (10,000)If exceeds:
✗ Collection failed
Error: Too many artifacts (12,456 > 10,000)
This usually indicates:
- Wildcard repos: '*' matched too many repositories
- Wildcard log groups: '*' matched too many log groups
Recommendation: Be more specific in source configurationControl Validation
Control Coverage Check
Verify controls can be satisfied:
evidence collect --dry-runOutput:
Testing control coverage...
CC6.1 - Logical Access Controls
✓ GitHub: Org 2FA enforcement
✓ AWS: IAM password policy
✗ Google Workspace: 2SV enforcement (source not configured)
CC6.6 - Access Removal/Modification
✓ GitHub: CODEOWNERS file
✗ Google Workspace: Admin roles (source not configured)
CC7.2 - Change Management
✓ GitHub: Branch protection
✓ AWS: CloudTrail status
✓ AWS: CloudWatch retention
Control Coverage:
- CC6.1: Partial (2/3 sources)
- CC6.6: Partial (1/2 sources)
- CC7.2: Complete (3/3 sources)
⚠ Warning: Some controls have partial coverage
Recommendation: Configure google_workspace source for complete coveragePass/Fail Criteria
CC6.1 - Logical Access Controls:
GitHub:
{
"two_factor_requirement_enabled": true // ✅ Must be true
}AWS:
{
"MinimumPasswordLength": 14, // ✅ Must be >= 12
"RequireSymbols": true, // ✅ Must be true
"RequireNumbers": true, // ✅ Must be true
"RequireUppercaseCharacters": true, // ✅ Must be true
"RequireLowercaseCharacters": true // ✅ Must be true
}Evaluation:
Evaluating CC6.1...
✓ GitHub 2FA: enabled
✓ AWS password length: 14 >= 12
✓ AWS password complexity: all requirements met
✓ CC6.1: PassCC7.2 - Change Management:
GitHub:
{
"required_pull_request_reviews": {
"required_approving_review_count": 2 // ✅ Must be >= 1
},
"required_status_checks": {
"strict": true, // ✅ Must be true
"contexts": ["ci/tests"] // ✅ Must have at least one
}
}AWS:
{
"IsLogging": true, // ✅ Must be true
"LatestDeliveryAttemptSucceeded": "2026-01-09T12:30:00Z" // ✅ Must be recent
}Evaluation:
Evaluating CC7.2...
✓ GitHub branch protection: 2 required reviewers
✓ GitHub status checks: enabled, 1 check required
✓ AWS CloudTrail: logging active
✓ AWS CloudTrail: last delivery 2 minutes ago
✓ CC7.2: PassCustom Policy Rules (Future)
Coming in v0.2:
# evidence.yaml
policy:
rules:
- name: require-codeowners
control: CC6.6
source: github
condition: |
artifacts['codeowners'].exists == true
- name: minimum-reviewers
control: CC7.2
source: github
condition: |
artifacts['branch_protection'].required_approving_review_count >= 3
- name: cloudtrail-encryption
control: CC7.2
source: aws
condition: |
artifacts['cloudtrail_config'].KmsKeyId != nullEvaluation output:
Evaluating custom policies...
✓ require-codeowners: Pass (23/25 repos)
✗ minimum-reviewers: Fail (12/25 repos have < 3 reviewers)
✓ cloudtrail-encryption: Pass
Custom Policy: Partial Pass (2/3 rules)Policy Bypass (Not Recommended)
Some validations can be bypassed for debugging:
# evidence.yaml (development only!)
policy:
bypass:
- scope_validation # Skip scope checks
- size_validation # Skip size limits
- content_validation # Skip forbidden content detectionWarning:
⚠ WARNING: Policy bypass enabled
The following validations are disabled:
- scope_validation
- size_validation
- content_validation
This is ONLY for debugging. Never use in production.
Bundles created with policy bypass may not be compliant.Use cases:
- Local testing
- Debugging collection issues
- Understanding what SDK collects
Never use in production or for compliance purposes.
Audit Logging
All policy decisions are logged:
{
"timestamp": "2026-01-09T12:35:00Z",
"event": "policy_evaluation",
"stage": "pre_collection",
"validations": [
{
"rule": "configuration_schema",
"result": "pass"
},
{
"rule": "github_token_scopes",
"result": "pass",
"scopes": ["repo:read", "read:org"]
},
{
"rule": "signing_key_permissions",
"result": "pass",
"permissions": "600"
}
]
}Log location:
- Console output (collection time)
run.jsonin bundle (permanent record)- Platform audit log (after upload)
Best Practices
1. Run Dry Run Before Production
# Test configuration and coverage
evidence collect --dry-run
# Review output for warnings
# Fix any issues before actual collection2. Monitor Policy Warnings
Don't ignore warnings:
⚠ Warning: Branch protection not configured for repository: acme/sandboxAction:
- Review repository
- Add branch protection if needed
- Or exclude from collection if not production
3. Regular Policy Audits
Monthly checklist:
- Review control coverage
- Verify all controls passing
- Check for new policy warnings
- Update configuration if needed
4. Test After Configuration Changes
After updating evidence.yaml:
# Validate configuration
evidence collect --dry-run
# Test actual collection (small scope first)
evidence collect
# Verify bundle
evidence verify evidence-bundle-*.tar.gzTroubleshooting
Configuration Validation Failed
Symptom:
✗ Configuration validation failed
- sources.github.org: required field missingSolution:
- Check evidence.yaml syntax
- Verify all required fields present
- Run
evidence collect --dry-runto test
Forbidden Content Detected
Symptom:
✗ Forbidden content detected
File: sources/github/secrets.json
Pattern: AWS access keyCause:
- Configuration error collecting wrong data
- Bug in SDK connector
Solution:
- Report to evidence SDK team
- Do NOT use bundle for compliance
- Investigate why secrets were collected
Control Coverage Incomplete
Symptom:
⚠ CC6.1: Partial coverage (1/3 sources)Solution:
- Add missing sources to configuration
- Or accept partial coverage (document why)
- Consult with compliance team
See Also
- Configuration Guide - Configure evidence.yaml
- SOC 2 Controls - Control pass criteria
- Connectors - Source connector details
- Troubleshooting - Common issues