stemedb/docs/rfcs/rfc-001-enterprise-policy-aliases.md
jordan 41c676a78e feat: Aphoria enterprise features + ontology SDK + file length compliance
Enterprise Features:
- Hosted mode with remote sync for team pattern aggregation
- Community sharing with privacy-preserving anonymization
- LLM-based semantic claim extraction with Gemini integration
- Pattern learning with promotion to declarative extractors
- High-entropy secrets extractor with configurable thresholds
- Auth bypass and insecure cookies extractors

Module Refactoring:
- Split oversized files to comply with 500-line limit
- Config split: types/core.rs, types/extractors.rs, types/hosted.rs, etc.
- Handlers split: scan.rs, policy.rs, report.rs modules
- Extractors split: declarative/, high_entropy_secrets/, insecure_cookies/
- Learning split: store modules with metrics and persistence

SDK & Ontology:
- stemedb-ontology SDK with fluent builders and StemeDB client
- Pharma domain extractors for FDA Orange Book data
- Consumer health UAT test infrastructure

Code Quality:
- Fixed clippy warnings (needless_borrows_for_generic_args)
- Added KVStore trait imports where needed
- Fixed utoipa path re-exports for OpenAPI docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 12:55:29 -07:00

30 KiB
Raw Permalink Blame History

RFC-001: Enterprise Policy Alias System

Date: 2026-02-05 Status: Proposed Author: Aphoria Team Target Audience: Enterprise security teams, CTOs, and development organizations


Executive Summary

Enterprise security teams need to enforce organization-wide security policies across diverse development teams using different programming languages and project structures. The current tail-path matching system works well for bundled RFC/OWASP corpus but fails when security teams create custom policies using logical hierarchies (e.g., code://standards/tls/*) that don't align with extractor output (e.g., code://rust/myapp/tls/*). This RFC proposes Policy Aliases—an explicit mapping layer in Trust Packs that bridges the gap between enterprise policy hierarchies and code extractor conventions, enabling seamless cross-language policy enforcement with full audit trails and cryptographic verification.


Problem Statement

Current State: Tail-Path Matching Works for Bundled Corpus

Aphoria uses a tail-path matching algorithm that extracts the last two segments of a subject path to create index keys. This enables cross-scheme concept matching:

RFC source:     "rfc://5246/tls/cert_verification"
Code extractor: "code://rust/myapp/tls/cert_verification"

Both produce key: "tls/cert_verification::enabled"

This works because extractors are intentionally designed to align with RFC/OWASP hierarchies. The bundled authoritative corpus and code extractors share a common conceptual vocabulary.

The Enterprise Gap: Policy Hierarchies Don't Match Extractor Output

When enterprise security teams create their own policies, they naturally organize them using logical domains, not language-qualified paths:

# Security team's mental model
subject = "code://standards/tls/cert_verification"   # Standards
subject = "code://internal/exceptions/md5_allowed"   # Exceptions
subject = "code://vendor/aws/s3/public_access"       # Cloud rules

But code extractors produce language-qualified paths:

// Rust extractor output
concept_path: "code://rust/myapp/tls/cert_verification"

// Go extractor output
concept_path: "code://go/myapp/tls/cert_verification"

Tail-path matching fails:

Source Path Extracted Key
Policy code://standards/tls/cert_verification tls/cert_verification::enabled
Code code://rust/myapp/tls/cert_verification myapp/tls::enabled

The keys don't match because they extract different segments: standards/tls vs rust/myapp/tls.

Business Impact

This mismatch has serious consequences for enterprises:

  1. Policy Enforcement Broken: Security standards don't match code violations
  2. Compliance Risk: Audits fail when policies exist but aren't enforced
  3. Adoption Blocker: Enterprises can't use Trust Packs for cross-team governance
  4. False Security: Teams think they're protected when policies aren't actually matching

Real-world scenario: A security team exports a signed Trust Pack with TLS requirements. Development teams import it and run scans. Zero conflicts are reported—not because the code is compliant, but because the matching algorithm never connects policy to code.


Design Goals

  1. Preserve Tail-Path Matching: The fast O(1) lookup for bundled corpus must remain the default path
  2. Explicit Over Implicit: Policy authors must explicitly define matches—no magic auto-aliasing
  3. Cryptographically Verified: Aliases are part of the signed Trust Pack, tamper-resistant
  4. Zero Dev Team Configuration: Import a Trust Pack URL, scanning works—no per-team setup
  5. Full Audit Trail: Every conflict traces back to specific policy, alias pattern, and issuer
  6. Backward Compatible: Existing Trust Packs without aliases continue working unchanged

Technical Architecture

Current: Tail-Path Matching

┌─────────────────────────────────────────────────────────────┐
│                      Scan Flow                               │
│                                                              │
│   1. Extractor produces:                                     │
│      concept_path: "code://rust/myapp/tls/cert_verification" │
│      predicate: "enabled"                                    │
│      value: false                                            │
│                                                              │
│   2. ConceptIndex::lookup() computes key:                    │
│      key = "myapp/tls::enabled" (last 2 segments)            │
│                                                              │
│   3. Hash table lookup: O(1)                                 │
│      result = index.get("myapp/tls::enabled")                │
│                                                              │
│   4. If match found → conflict detection                     │
│      If no match → skip (no authoritative source)            │
└─────────────────────────────────────────────────────────────┘

Strengths:

  • O(1) constant-time lookups
  • Zero configuration required
  • Cross-language matching for RFC-aligned extractors

Limitation:

  • Only works when policy paths and extractor paths share the same final segments

Extension: Policy Aliases

Policy Aliases add a fallback matching layer that activates only when tail-path matching fails:

┌─────────────────────────────────────────────────────────────┐
│                 Extended Matching Flow                       │
│                                                              │
│   1. Try tail-path match (existing, fast path)               │
│      key = make_key(subject, predicate)                      │
│      if index.contains(key) → return match                   │
│                                                              │
│   2. Try policy alias patterns (fallback path)               │
│      for alias in trust_pack.policy_aliases:                 │
│        for pattern in alias.target_patterns:                 │
│          if glob_match(pattern, subject):                    │
│            key = make_key(alias.policy_path, predicate)      │
│            if index.contains(key) → return match             │
│                                                              │
│   3. No match → no authoritative source for this claim       │
└─────────────────────────────────────────────────────────────┘

Matching Algorithm

Pseudocode:

fn lookup_with_policy_aliases(
    index: &ConceptIndex,
    subject: &str,
    predicate: &str,
    policy_aliases: &[PolicyAlias],
) -> Option<&Vec<Assertion>> {
    // 1. Fast path: direct tail-path match (O(1))
    if let Some(result) = index.lookup(subject, predicate) {
        return Some(result);
    }

    // 2. Fallback path: policy alias expansion (O(P × A))
    //    P = patterns per alias, A = number of aliases
    for alias in policy_aliases {
        if subject_matches_any_pattern(subject, &alias.target_patterns) {
            if let Some(result) = index.lookup(&alias.policy_path, predicate) {
                return Some(result);
            }
        }
    }

    None
}

fn subject_matches_any_pattern(subject: &str, patterns: &[String]) -> bool {
    patterns.iter().any(|pattern| glob_match(pattern, subject))
}

fn glob_match(pattern: &str, subject: &str) -> bool {
    // Split both into segments
    let pattern_parts: Vec<&str> = pattern.split('/').collect();
    let subject_parts: Vec<&str> = subject.split('/').collect();

    // Must have same segment count
    if pattern_parts.len() != subject_parts.len() {
        return false;
    }

    // Match segment-by-segment; "*" matches any single segment
    pattern_parts.iter()
        .zip(subject_parts.iter())
        .all(|(p, s)| *p == "*" || *p == *s)
}

Step-by-step example:

Input:
  subject = "code://rust/backend-api/tls/cert_verification"
  predicate = "enabled"
  alias = PolicyAlias {
    policy_path: "code://standards/tls/cert_verification",
    target_patterns: ["code://rust/*/tls/cert_verification"],
  }

Step 1: Tail-path match attempt
  key = "backend-api/tls::enabled"
  index.lookup("backend-api/tls::enabled") → None

Step 2: Alias pattern matching
  Pattern: "code://rust/*/tls/cert_verification"
  Subject: "code://rust/backend-api/tls/cert_verification"

  Segment comparison:
    "code:" == "code:" ✓
    "" == "" ✓ (after first slash)
    "rust" == "rust" ✓
    "*" matches "backend-api" ✓ (wildcard)
    "tls" == "tls" ✓
    "cert_verification" == "cert_verification" ✓

  Pattern matches!

Step 3: Lookup using policy_path
  key = "tls/cert_verification::enabled"
  index.lookup("tls/cert_verification::enabled") → Some([Assertion])

Result: Match found via policy alias

Trust Pack Schema Extension

Current Schema

#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
#[archive(check_bytes)]
pub struct TrustPack {
    pub header: PackHeader,
    pub assertions: Vec<Assertion>,
    pub aliases: Vec<ConceptAlias>,  // Existing: concept-level aliases
    pub signature: [u8; 64],         // Ed25519 signature
}

pub struct PackHeader {
    pub name: String,
    pub version: String,
    pub issuer_id: [u8; 32],  // Ed25519 public key
    pub timestamp: u64,       // Unix timestamp
}

Extended Schema

#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
#[archive(check_bytes)]
pub struct TrustPack {
    pub header: PackHeader,
    pub assertions: Vec<Assertion>,
    pub aliases: Vec<ConceptAlias>,
    pub policy_aliases: Vec<PolicyAlias>,  // NEW: Policy-level aliases
    pub signature: [u8; 64],
}

/// Maps policy assertion paths to extractor output patterns.
///
/// Enables enterprise security teams to define standards using logical hierarchies
/// (e.g., "code://standards/tls/*") that match extractor output
/// (e.g., "code://rust/myapp/tls/*").
#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
#[archive(check_bytes)]
pub struct PolicyAlias {
    /// The policy path used in assertions.
    /// Example: "code://standards/tls/cert_verification"
    pub policy_path: String,

    /// Glob patterns that should resolve to this policy path.
    /// Supports single-segment wildcards (*) only.
    ///
    /// Examples:
    /// - "code://rust/*/tls/cert_verification"  (any Rust project)
    /// - "code://*/myapp/tls/cert_verification" (any language, specific project)
    /// - "code://*/*/tls/cert_verification"     (any language, any project)
    pub target_patterns: Vec<String>,
}

Backward Compatibility:

// Deserialization with default for missing field
impl Default for TrustPack {
    fn default() -> Self {
        TrustPack {
            header: PackHeader::default(),
            assertions: vec![],
            aliases: vec![],
            policy_aliases: vec![],  // Empty = no policy aliases = current behavior
            signature: [0u8; 64],
        }
    }
}

Existing Trust Packs without policy_aliases field will deserialize with an empty vector, preserving current behavior.


CLI Interface

Adding Policy Aliases

# Add a single alias with multiple target patterns
aphoria policy add-alias \
  --pack security-standards.pack \
  --policy-path "code://standards/tls/cert_verification" \
  --target "code://rust/*/tls/cert_verification" \
  --target "code://go/*/tls/cert_verification" \
  --target "code://python/*/tls/cert_verification"

Options:

Flag Description
--pack <path> Path to Trust Pack file
--policy-path <uri> Policy assertion path to alias
--target <pattern> Glob pattern (can be specified multiple times)
--output <path> Output path (default: overwrite input)

Listing Policy Aliases

aphoria policy list-aliases --pack security-standards.pack

Output:

Policy Aliases in "Acme Security Standards" v1.0.0:

1. code://standards/tls/cert_verification
   Targets:
     - code://rust/*/tls/cert_verification
     - code://go/*/tls/cert_verification
     - code://python/*/tls/cert_verification

2. code://standards/jwt/audience_validation
   Targets:
     - code://*/*/jwt/audience_validation

Total: 2 policy aliases

Validating Aliases

aphoria policy validate-aliases --pack security-standards.pack

Output:

Validating policy aliases...

✓ code://rust/*/tls/cert_verification - valid glob pattern
✓ code://go/*/tls/cert_verification - valid glob pattern
✓ code://python/*/tls/cert_verification - valid glob pattern

⚠ Warning: No assertions found for policy path "code://standards/jwt/audience_validation"
  (alias exists but no corresponding assertion)

Validation complete: 3 valid, 0 invalid, 1 warning

Enterprise Workflow

Step 1: Security Team Creates Standards (Golden Repo)

cd security-standards/

# Create TLS certificate verification requirement
aphoria bless \
  --subject "code://standards/tls/cert_verification" \
  --predicate "enabled" \
  --value true \
  --reason "RFC 5246 compliance - TLS certificate verification required"

# Create JWT audience validation requirement
aphoria bless \
  --subject "code://standards/jwt/audience_validation" \
  --predicate "enabled" \
  --value true \
  --reason "RFC 7519 compliance - JWT audience claim must be validated"

Step 2: Security Team Adds Policy Aliases

# Export unsigned pack
aphoria policy export --name "Acme-Security-Standards" --output acme-v1.0.pack

# Add aliases for all supported languages
aphoria policy add-alias \
  --pack acme-v1.0.pack \
  --policy-path "code://standards/tls/cert_verification" \
  --target "code://rust/*/tls/cert_verification" \
  --target "code://go/*/tls/cert_verification" \
  --target "code://python/*/tls/cert_verification" \
  --target "code://java/*/tls/cert_verification"

aphoria policy add-alias \
  --pack acme-v1.0.pack \
  --policy-path "code://standards/jwt/audience_validation" \
  --target "code://*/*/jwt/audience_validation"

Step 3: Security Team Publishes Pack

# Upload to internal artifact server
aws s3 cp acme-v1.0.pack s3://acme-policies/acme-v1.0.pack --acl private

# Or publish to internal registry
curl -X POST https://policy-registry.acme.com/packs \
  -H "Authorization: Bearer $SECURITY_TEAM_TOKEN" \
  --data-binary @acme-v1.0.pack

Step 4: Development Team Configures Project

File: aphoria.toml

[policies]
sources = [
    "https://policy-registry.acme.com/packs/acme-v1.0.pack"
]

# Or using S3 presigned URL
# sources = [
#     "s3://acme-policies/acme-v1.0.pack"
# ]

That's it. No other configuration needed.

Step 5: Development Team Scans Code

aphoria scan --mode persistent

Console Output:

┌─────────────────────────────────────────────────────────────────┐
│ Aphoria Security Scan Report                                    │
├─────────────────────────────────────────────────────────────────┤
│ Project: backend-api                                            │
│ Files:   42                                                     │
│ Claims:  127                                                    │
├─────────────────────────────────────────────────────────────────┤
│ BLOCK (1)                                                       │
├─────────────────────────────────────────────────────────────────┤
│ src/main.rs:42                                                  │
│                                                                 │
│ code://rust/backend-api/tls/cert_verification                   │
│                                                                 │
│   Code asserts:  enabled = false  (confidence: 0.95)            │
│   Authority:     enabled = true   (confidence: 1.00)            │
│                                                                 │
│   Source: Acme-Security-Standards v1.0.0                        │
│   Issuer: a1b2c3d4e5f6...                                       │
│   Policy: code://standards/tls/cert_verification                │
│                                                                 │
│   Matched via policy alias:                                     │
│     Pattern: code://rust/*/tls/cert_verification                │
│                                                                 │
│   Verdict: BLOCK                                                │
│   Recommendation: Enable TLS certificate verification           │
└─────────────────────────────────────────────────────────────────┘

Exit code: 1

Step 6: CI/CD Integration

GitHub Actions:

name: Security Scan

on: [push, pull_request]

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

      - name: Install Aphoria
        run: cargo install aphoria

      - name: Run Security Scan
        run: aphoria scan --mode persistent --exit-code
        # Exit code 1 = BLOCK verdict found → build fails

Security Considerations

Signature Verification

Policy aliases are included in the signed Trust Pack:

fn sign_trust_pack(pack: &TrustPack, key: &SigningKey) -> [u8; 64] {
    // Signature covers ALL fields including policy_aliases
    let content = serialize(&TrustPackContent {
        header: pack.header.clone(),
        assertions: pack.assertions.clone(),
        aliases: pack.aliases.clone(),
        policy_aliases: pack.policy_aliases.clone(),  // Included
    });

    key.sign(&content).to_bytes()
}

Guarantee: If anyone tampers with policy aliases, signature verification fails.

Audit Trail

Every conflict report includes:

  1. Pack Name & Version: Which Trust Pack triggered the conflict
  2. Issuer ID: Ed25519 public key of the signing authority
  3. Policy Path: The canonical policy assertion that was matched
  4. Matched Pattern: Which glob pattern caused the match
  5. Timestamp: When the Trust Pack was created

This enables forensic analysis: "Why did this build fail?" → "Because pattern X in pack Y matched code Z."

Tamper Resistance

Trust Pack Integrity Chain:

1. Security team creates assertions in local Episteme store
2. `aphoria policy export` serializes + signs with team's Ed25519 key
3. Pack distributed via any channel (S3, registry, etc.)
4. Dev team's `aphoria scan` verifies signature before using
5. If signature invalid → pack rejected, scan fails with clear error

Privilege Separation

Actor Can Do Cannot Do
Security Team Create assertions, add aliases, sign packs Modify dev team code
Dev Team Import packs, run scans, acknowledge conflicts Modify pack contents, bypass policies
CI/CD Run scans, fail builds Override BLOCK verdicts

Performance Analysis

Complexity Analysis

Operation Time Complexity Space Complexity
Tail-path match (existing) O(1) O(N) where N = assertions
Policy alias fallback O(P × A) O(A) where A = aliases
Total lookup O(1) + O(P × A) O(N + A)

Where:

  • P = patterns per alias (typically 3-5)
  • A = total number of aliases (typically 10-50)

Expected Performance:

  • 95% of lookups hit fast path (tail-path match)
  • 5% of lookups fall through to alias matching
  • Alias matching adds ~0.1ms per lookup (simple string comparison)

Benchmark Targets

Metric Target Rationale
Scan time increase < 5% Alias matching should be negligible
Memory overhead < 10 KB per pack Aliases are small strings
Policy download time < 1s Packs cached after first download

Real-World Estimate

For a typical enterprise deployment:

  • 50 policy assertions
  • 20 policy aliases
  • 5 patterns per alias (100 patterns total)
  • 10,000 code claims per scan

Calculations:

  • Fast path hits: ~9,500 claims (95%)
  • Alias fallback: ~500 claims
  • Alias comparisons: 500 × 100 = 50,000 string comparisons
  • String comparison time: ~1 microsecond each
  • Total alias overhead: ~50ms per scan

Conclusion: Negligible impact on scan time.


Backward Compatibility

Migration Path

No migration required. The change is purely additive:

  1. Existing Trust Packs: Deserialize with empty policy_aliases vector
  2. Existing Scans: Empty aliases = skip alias fallback = current behavior
  3. Existing CI/CD: No changes to configuration or commands
  4. Existing Extractors: No changes to output format

Zero-Friction Adoption

Scenario Required Changes
New enterprise deployment Add policy_aliases to Trust Pack
Existing small team None (bundled corpus works)
Existing enterprise with custom pack Add aliases, re-sign, redistribute

Version Negotiation

Trust Packs include a version field in the header:

pub struct PackHeader {
    pub schema_version: u8,  // 1 = original, 2 = with policy_aliases
    // ...
}

Older Aphoria versions encountering schema v2 packs will:

  1. Log a warning about unknown field
  2. Ignore policy_aliases (graceful degradation)
  3. Continue with tail-path matching only

Alternative Approaches Considered

Alternative 1: Normalize Extractor Output

Idea: Make extractors output "canonical" paths that match standards.

// Instead of: "code://rust/myapp/tls/cert_verification"
// Output:     "code://standards/tls/cert_verification"

Why rejected:

  • Loses language context (rust/myapp) needed for file location
  • Breaks existing aliases and observations
  • Forces extractors to know about ALL standards
  • Violates separation of concerns

Alternative 2: Flexible Tail-Path Length

Idea: Try matching with N=1, N=2, N=3 segments.

// Try multiple keys
"cert_verification::enabled"           // N=1
"tls/cert_verification::enabled"       // N=2
"myapp/tls/cert_verification::enabled" // N=3

Why rejected:

  • Ambiguous matches (which key wins?)
  • Performance impact (3x lookups)
  • Doesn't solve semantic differences
  • No explicit control over matching behavior

Alternative 3: Auto-Discover Aliases

Idea: Automatically create aliases during scan when tail-path matches but full path differs.

// Scan detects potential match, auto-creates alias
if tail_match && !full_match {
    auto_alias(claim.concept_path, assertion.subject);
}

Why rejected:

  • Implicit behavior (hard to debug)
  • No security team approval (bypasses policy control)
  • May create false positives
  • Violates "explicit over implicit" principle

Future consideration: Could be implemented as suggestions (not automatic), shown in scan output.


Implementation Roadmap

Phase 1: Schema Extension (Day 1)

  1. Add PolicyAlias type to policy.rs
  2. Extend TrustPack struct with policy_aliases field
  3. Update serialization/deserialization
  4. Update signature computation to include aliases
  5. Add unit tests for schema changes

Deliverable: Trust Packs can store and serialize policy aliases.

Phase 2: Pattern Matching (Day 1-2)

  1. Implement glob_match() function
  2. Add lookup_with_policy_aliases() to ConceptIndex
  3. Write comprehensive test suite for pattern matching
  4. Handle edge cases (empty patterns, invalid syntax)

Deliverable: Pattern matching algorithm works correctly.

Phase 3: Scan Integration (Day 2)

  1. Load policy aliases from imported Trust Packs
  2. Pass aliases to conflict detection
  3. Include alias info in conflict reports
  4. Update all output formats (JSON, table, markdown, SARIF)

Deliverable: Scans detect conflicts via policy aliases.

Phase 4: CLI Tooling (Day 3)

  1. Implement policy add-alias command
  2. Implement policy list-aliases command
  3. Implement policy validate-aliases command
  4. Update help text and documentation

Deliverable: Security teams can manage aliases via CLI.

Phase 5: Documentation & UAT (Day 3-4)

  1. Update user documentation
  2. Write enterprise deployment guide
  3. Create UAT scenario with real enterprise workflow
  4. Validate end-to-end functionality

Deliverable: Complete documentation and validated implementation.


Open Questions

Q1: Should we support recursive wildcards?

Current proposal: Single-segment wildcards only (*)

"code://rust/*/tls/*"        ✓ Supported
"code://rust/**/tls"         ✗ Not supported (yet)

Trade-off: Recursive wildcards (**) are more flexible but harder to reason about. Start simple, add if needed.

Decision requested: Defer recursive wildcards to future enhancement?

Q2: What happens when multiple aliases match?

Current proposal: First match wins (aliases processed in declaration order).

Alternative: Most specific match wins (fewer wildcards = higher priority).

Trade-off: First-match is simpler but requires security teams to order aliases carefully.

Decision requested: First-match or most-specific?

Q3: Should invalid patterns fail Trust Pack creation?

Current proposal: Yes—validate patterns at creation time, reject invalid glob syntax.

Alternative: Warn but allow (lenient), fail at scan time.

Trade-off: Fail-fast is better for security teams but may be inconvenient during iteration.

Decision requested: Strict validation at creation time?

Q4: Should we cache pattern match results?

Current proposal: Recompute on each lookup (simple, no cache invalidation issues).

Alternative: Cache subject → policy_path map per scan.

Trade-off: Caching improves performance but adds complexity.

Decision requested: Defer optimization until benchmarks show need?


Appendix: Example Scenarios

Scenario A: Multi-Language TLS Enforcement

Security team creates:

aphoria bless \
  --subject "code://standards/tls/cert_verification" \
  --predicate "enabled" \
  --value true

aphoria policy add-alias \
  --pack standards.pack \
  --policy-path "code://standards/tls/cert_verification" \
  --target "code://rust/*/tls/cert_verification" \
  --target "code://go/*/tls/cert_verification" \
  --target "code://python/*/tls/cert_verification" \
  --target "code://java/*/tls/cert_verification" \
  --target "code://kotlin/*/tls/cert_verification"

Rust team's code:

// src/client.rs
let client = reqwest::Client::builder()
    .danger_accept_invalid_certs(true)  // Violation!
    .build()?;

Extractor output:

concept_path: "code://rust/api-service/tls/cert_verification"
predicate: "enabled"
value: false

Scan result:

BLOCK: TLS cert verification disabled
  Policy: code://standards/tls/cert_verification
  Matched via: code://rust/*/tls/cert_verification
  Source: Acme-Security-Standards v1.0.0

Scenario B: Cloud-Specific Rules

Security team creates:

aphoria bless \
  --subject "code://vendor/aws/s3/public_access" \
  --predicate "blocked" \
  --value true \
  --reason "S3 buckets must not allow public access"

aphoria policy add-alias \
  --pack cloud-rules.pack \
  --policy-path "code://vendor/aws/s3/public_access" \
  --target "code://*/*/aws/s3/public_access" \
  --target "code://*/*/cloud/storage/s3/public"

Dev team's Terraform:

resource "aws_s3_bucket_public_access_block" "example" {
  bucket = aws_s3_bucket.example.id
  block_public_acls = false  # Violation!
}

Scan detects via alias matching.


Scenario C: Internal Policy Exceptions

Security team creates exception:

# Allow MD5 for specific legacy system
aphoria bless \
  --subject "code://internal/exceptions/legacy-auth/md5" \
  --predicate "allowed" \
  --value true \
  --reason "Legacy auth system requires MD5 until migration complete"

aphoria policy add-alias \
  --pack exceptions.pack \
  --policy-path "code://internal/exceptions/legacy-auth/md5" \
  --target "code://rust/legacy-auth-service/crypto/md5" \
  --target "code://go/legacy-auth-service/crypto/md5"

Result: Only the specific legacy service can use MD5; all other services still blocked.


Glossary

Term Definition
Assertion A claim about a subject-predicate-object relationship
Trust Pack Signed bundle of assertions and aliases for distribution
Policy Alias Mapping from policy path to extractor output patterns
Tail-Path Matching Algorithm using last 2 path segments for index lookup
Glob Pattern String pattern with * wildcards for matching paths
Issuer ID Ed25519 public key identifying Trust Pack signer
BLOCK Verdict Scan result indicating policy violation that should halt build

References