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>
788 lines
20 KiB
Markdown
788 lines
20 KiB
Markdown
# Policy Alias Implementation Guide
|
|
|
|
**Related:** [Concept Matching Analysis](./concept-matching-analysis.md)
|
|
**Status:** Implementation Ready
|
|
**Estimated Effort:** 2-3 days
|
|
|
|
---
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Schema Extension (Day 1, Morning)
|
|
|
|
**Goal:** Add `PolicyAlias` type and extend `TrustPack`.
|
|
|
|
#### 1.1 Define PolicyAlias Type
|
|
|
|
**File:** `applications/aphoria/src/policy.rs`
|
|
|
|
```rust
|
|
/// 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 (e.g., "code://standards/tls/cert_verification").
|
|
pub policy_path: String,
|
|
|
|
/// Glob patterns that should resolve to this policy path.
|
|
/// Supports '*' wildcard for single-segment match.
|
|
///
|
|
/// Examples:
|
|
/// - "code://rust/*/tls/cert_verification" (matches any project)
|
|
/// - "code://*/myapp/tls/cert_verification" (matches any language)
|
|
/// - "code://rust/myapp/*/cert_verification" (matches any module)
|
|
pub target_patterns: Vec<String>,
|
|
}
|
|
```
|
|
|
|
#### 1.2 Extend TrustPack
|
|
|
|
**File:** `applications/aphoria/src/policy.rs`
|
|
|
|
```rust
|
|
#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
|
|
#[archive(check_bytes)]
|
|
pub struct TrustPack {
|
|
pub header: PackHeader,
|
|
pub assertions: Vec<Assertion>,
|
|
pub aliases: Vec<ConceptAlias>,
|
|
|
|
/// Policy-level aliases for matching extractor output to policy paths.
|
|
/// Optional: Empty vec = no policy aliases (backward compatible).
|
|
pub policy_aliases: Vec<PolicyAlias>,
|
|
|
|
pub signature: [u8; 64],
|
|
}
|
|
```
|
|
|
|
#### 1.3 Update TrustPack Constructor
|
|
|
|
```rust
|
|
impl TrustPack {
|
|
pub fn new(
|
|
name: String,
|
|
version: String,
|
|
assertions: Vec<Assertion>,
|
|
aliases: Vec<ConceptAlias>,
|
|
policy_aliases: Vec<PolicyAlias>, // NEW
|
|
signing_key: &SigningKey,
|
|
) -> Result<Self, AphoriaError> {
|
|
// ... existing timestamp/issuer logic
|
|
|
|
let temp_pack = TrustPack {
|
|
header: header.clone(),
|
|
assertions: assertions.clone(),
|
|
aliases: aliases.clone(),
|
|
policy_aliases: policy_aliases.clone(), // NEW
|
|
signature: [0u8; 64],
|
|
};
|
|
|
|
// ... existing signing logic
|
|
|
|
Ok(TrustPack {
|
|
header,
|
|
assertions,
|
|
aliases,
|
|
policy_aliases, // NEW
|
|
signature
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
cargo test -p aphoria policy::tests::trust_pack_with_policy_aliases
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 2: Pattern Matching (Day 1, Afternoon)
|
|
|
|
**Goal:** Implement glob-based pattern matching for policy aliases.
|
|
|
|
#### 2.1 Add Glob Matching Function
|
|
|
|
**File:** `applications/aphoria/src/episteme/concept_index.rs`
|
|
|
|
```rust
|
|
/// Check if a subject matches a glob pattern with '*' wildcard.
|
|
///
|
|
/// Supports single-segment wildcards only (not recursive `**`).
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// assert!(glob_match("code://rust/*/tls/cert_verification", "code://rust/myapp/tls/cert_verification"));
|
|
/// assert!(glob_match("code://*/myapp/tls/*", "code://python/myapp/tls/min_version"));
|
|
/// assert!(!glob_match("code://rust/*/tls", "code://go/myapp/tls/cert_verification"));
|
|
/// ```
|
|
fn glob_match(pattern: &str, subject: &str) -> bool {
|
|
let pattern_parts: Vec<&str> = pattern.split('/').collect();
|
|
let subject_parts: Vec<&str> = subject.split('/').collect();
|
|
|
|
if pattern_parts.len() != subject_parts.len() {
|
|
return false;
|
|
}
|
|
|
|
pattern_parts.iter().zip(subject_parts.iter()).all(|(p, s)| {
|
|
*p == "*" || *p == *s
|
|
})
|
|
}
|
|
```
|
|
|
|
#### 2.2 Extend ConceptIndex Lookup
|
|
|
|
**File:** `applications/aphoria/src/episteme/concept_index.rs`
|
|
|
|
```rust
|
|
use crate::policy::PolicyAlias;
|
|
|
|
impl ConceptIndex {
|
|
/// Look up assertions with policy alias fallback.
|
|
///
|
|
/// Algorithm:
|
|
/// 1. Try direct tail-path match (existing behavior)
|
|
/// 2. If no match, try each policy alias pattern
|
|
/// 3. If pattern matches subject, lookup using policy_path
|
|
/// 4. Return first match (policy aliases processed in order)
|
|
pub fn lookup_with_policy_aliases(
|
|
&self,
|
|
subject: &str,
|
|
predicate: &str,
|
|
policy_aliases: &[PolicyAlias],
|
|
) -> Option<&Vec<Assertion>> {
|
|
// Try direct tail-path match first (fast path)
|
|
if let Some(result) = self.lookup(subject, predicate) {
|
|
return Some(result);
|
|
}
|
|
|
|
// Try policy alias patterns (fallback)
|
|
for alias in policy_aliases {
|
|
// Check if any pattern matches the subject
|
|
let pattern_matches = alias.target_patterns.iter().any(|pattern| {
|
|
glob_match(pattern, subject)
|
|
});
|
|
|
|
if pattern_matches {
|
|
// Look up using the policy path instead
|
|
if let Some(result) = self.lookup(&alias.policy_path, predicate) {
|
|
return Some(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
}
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
cargo test -p aphoria episteme::concept_index::tests::policy_alias_matching
|
|
```
|
|
|
|
**Test Cases:**
|
|
```rust
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_glob_match_wildcard() {
|
|
assert!(glob_match("code://rust/*/tls", "code://rust/myapp/tls"));
|
|
assert!(glob_match("code://*/myapp/tls", "code://rust/myapp/tls"));
|
|
assert!(!glob_match("code://rust/*/tls", "code://go/myapp/tls"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_policy_alias_lookup() {
|
|
// Policy assertion: "code://standards/tls/cert_verification"
|
|
let policy_assertion = Assertion {
|
|
subject: "code://standards/tls/cert_verification".to_string(),
|
|
predicate: "enabled".to_string(),
|
|
object: ObjectValue::Boolean(true),
|
|
// ... other fields
|
|
};
|
|
|
|
let index = ConceptIndex::build(&[policy_assertion]);
|
|
|
|
let alias = PolicyAlias {
|
|
policy_path: "code://standards/tls/cert_verification".to_string(),
|
|
target_patterns: vec![
|
|
"code://rust/*/tls/cert_verification".to_string(),
|
|
],
|
|
};
|
|
|
|
// Should match via alias
|
|
let result = index.lookup_with_policy_aliases(
|
|
"code://rust/myapp/tls/cert_verification",
|
|
"enabled",
|
|
&[alias],
|
|
);
|
|
|
|
assert!(result.is_some());
|
|
assert_eq!(result.unwrap().len(), 1);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 3: Integration (Day 2, Morning)
|
|
|
|
**Goal:** Wire policy aliases into scan flow.
|
|
|
|
#### 3.1 Pass Policy Aliases to ConceptIndex
|
|
|
|
**File:** `applications/aphoria/src/scan.rs`
|
|
|
|
```rust
|
|
async fn check_conflicts_persistent(
|
|
all_claims: &[ExtractedClaim],
|
|
project_root: &Path,
|
|
config: &AphoriaConfig,
|
|
sync: bool,
|
|
) -> Result<ConflictCheckResult, AphoriaError> {
|
|
// ... existing setup
|
|
|
|
// Load policies (Trust Packs)
|
|
let policy_manager = PolicyManager::new(&config.corpus.cache_dir);
|
|
let policies = policy_manager.load_policies(&config.policies)?;
|
|
|
|
// Extract policy aliases from all Trust Packs
|
|
let policy_aliases: Vec<PolicyAlias> = policies
|
|
.iter()
|
|
.flat_map(|pack| &pack.policy_aliases)
|
|
.cloned()
|
|
.collect();
|
|
|
|
info!(
|
|
policy_alias_count = policy_aliases.len(),
|
|
"Loaded policy aliases from Trust Packs"
|
|
);
|
|
|
|
// Build corpus and index
|
|
let mut corpus = create_authoritative_corpus(&signing_key);
|
|
let imported_assertions = episteme.fetch_authoritative_assertions().await?;
|
|
corpus.extend(imported_assertions);
|
|
let index = ConceptIndex::build(&corpus);
|
|
|
|
// Pass aliases to conflict checker
|
|
let conflicts = episteme
|
|
.check_conflicts_with_aliases(all_claims, config, &index, &policy_aliases)
|
|
.await?;
|
|
|
|
// ... rest of function
|
|
}
|
|
```
|
|
|
|
#### 3.2 Extend LocalEpisteme::check_conflicts
|
|
|
|
**File:** `applications/aphoria/src/episteme/local.rs`
|
|
|
|
```rust
|
|
use crate::policy::PolicyAlias;
|
|
|
|
impl LocalEpisteme {
|
|
/// Check conflicts with policy alias support.
|
|
pub async fn check_conflicts_with_aliases(
|
|
&self,
|
|
claims: &[ExtractedClaim],
|
|
config: &AphoriaConfig,
|
|
index: &ConceptIndex,
|
|
policy_aliases: &[PolicyAlias],
|
|
) -> Result<Vec<ConflictResult>, AphoriaError> {
|
|
// ... existing setup (fetch acks, etc.)
|
|
|
|
for claim in claims {
|
|
// Use extended lookup with policy aliases
|
|
let auth_assertions = match index.lookup_with_policy_aliases(
|
|
&claim.concept_path,
|
|
&claim.predicate,
|
|
policy_aliases,
|
|
) {
|
|
Some(assertions) => assertions,
|
|
None => continue,
|
|
};
|
|
|
|
// ... rest of conflict detection logic (unchanged)
|
|
}
|
|
|
|
// ... return results
|
|
}
|
|
}
|
|
```
|
|
|
|
**Backward Compatibility:**
|
|
```rust
|
|
// Keep existing method for ephemeral mode
|
|
pub async fn check_conflicts(
|
|
&self,
|
|
claims: &[ExtractedClaim],
|
|
config: &AphoriaConfig,
|
|
index: &ConceptIndex,
|
|
) -> Result<Vec<ConflictResult>, AphoriaError> {
|
|
// Delegate to new method with empty aliases
|
|
self.check_conflicts_with_aliases(claims, config, index, &[]).await
|
|
}
|
|
```
|
|
|
|
#### 3.3 Update EphemeralDetector
|
|
|
|
**File:** `applications/aphoria/src/episteme/ephemeral.rs`
|
|
|
|
```rust
|
|
pub struct EphemeralDetector {
|
|
// ... existing fields
|
|
policy_aliases: Vec<PolicyAlias>, // NEW
|
|
}
|
|
|
|
impl EphemeralDetector {
|
|
pub fn ingest_policies(&mut self, policies: &[TrustPack]) {
|
|
for pack in policies {
|
|
// Ingest assertions (existing)
|
|
self.ingest_authoritative(&pack.assertions);
|
|
|
|
// Ingest policy aliases (NEW)
|
|
self.policy_aliases.extend(pack.policy_aliases.clone());
|
|
}
|
|
}
|
|
|
|
pub fn check_conflicts(
|
|
&self,
|
|
claims: &[ExtractedClaim],
|
|
config: &AphoriaConfig,
|
|
) -> Vec<ConflictResult> {
|
|
let index = ConceptIndex::build(&self.corpus);
|
|
|
|
// Use policy aliases in lookup
|
|
for claim in claims {
|
|
let auth_assertions = index.lookup_with_policy_aliases(
|
|
&claim.concept_path,
|
|
&claim.predicate,
|
|
&self.policy_aliases,
|
|
);
|
|
// ... rest of conflict logic
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 4: CLI Tooling (Day 2, Afternoon)
|
|
|
|
**Goal:** Enable security teams to create policy aliases easily.
|
|
|
|
#### 4.1 Add `policy add-alias` Command
|
|
|
|
**File:** `applications/aphoria/src/types/command.rs`
|
|
|
|
```rust
|
|
#[derive(Debug, Subcommand)]
|
|
pub enum PolicyCommand {
|
|
// ... existing commands (export, import, list)
|
|
|
|
/// Add a policy alias to a Trust Pack.
|
|
///
|
|
/// Allows mapping extractor output patterns to policy assertion paths.
|
|
#[command(name = "add-alias")]
|
|
AddAlias {
|
|
/// Path to the Trust Pack file.
|
|
#[arg(short, long)]
|
|
pack: PathBuf,
|
|
|
|
/// Policy path (e.g., "code://standards/tls/cert_verification").
|
|
#[arg(long)]
|
|
policy_path: String,
|
|
|
|
/// Target pattern (e.g., "code://rust/*/tls/cert_verification").
|
|
/// Can be specified multiple times.
|
|
#[arg(long = "target")]
|
|
target_patterns: Vec<String>,
|
|
|
|
/// Output path for updated pack (default: overwrite input).
|
|
#[arg(short, long)]
|
|
output: Option<PathBuf>,
|
|
},
|
|
}
|
|
```
|
|
|
|
#### 4.2 Implement Handler
|
|
|
|
**File:** `applications/aphoria/src/policy_ops.rs`
|
|
|
|
```rust
|
|
use crate::policy::{PolicyAlias, TrustPack};
|
|
|
|
pub fn handle_policy_add_alias(
|
|
pack_path: &Path,
|
|
policy_path: String,
|
|
target_patterns: Vec<String>,
|
|
output_path: Option<&Path>,
|
|
signing_key: &SigningKey,
|
|
) -> Result<(), AphoriaError> {
|
|
// Load existing Trust Pack
|
|
let mut pack = TrustPack::load(pack_path)?;
|
|
|
|
info!(
|
|
pack = %pack.header.name,
|
|
version = %pack.header.version,
|
|
"Loaded Trust Pack"
|
|
);
|
|
|
|
// Validate patterns
|
|
for pattern in &target_patterns {
|
|
if !is_valid_glob_pattern(pattern) {
|
|
return Err(AphoriaError::Config(format!(
|
|
"Invalid glob pattern: {}",
|
|
pattern
|
|
)));
|
|
}
|
|
}
|
|
|
|
// Check if alias already exists (avoid duplicates)
|
|
let exists = pack.policy_aliases.iter().any(|a| {
|
|
a.policy_path == policy_path && a.target_patterns == target_patterns
|
|
});
|
|
|
|
if exists {
|
|
info!("Policy alias already exists, skipping");
|
|
return Ok(());
|
|
}
|
|
|
|
// Add new policy alias
|
|
let alias = PolicyAlias { policy_path: policy_path.clone(), target_patterns };
|
|
pack.policy_aliases.push(alias);
|
|
|
|
// Re-sign the pack (required because we modified it)
|
|
let new_pack = TrustPack::new(
|
|
pack.header.name,
|
|
pack.header.version,
|
|
pack.assertions,
|
|
pack.aliases,
|
|
pack.policy_aliases,
|
|
signing_key,
|
|
)?;
|
|
|
|
// Save to output path (or overwrite input)
|
|
let save_path = output_path.unwrap_or(pack_path);
|
|
new_pack.save(save_path)?;
|
|
|
|
info!(
|
|
policy_path,
|
|
output = %save_path.display(),
|
|
"Added policy alias to Trust Pack"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn is_valid_glob_pattern(pattern: &str) -> bool {
|
|
// Check for balanced segments (no double slashes, etc.)
|
|
!pattern.is_empty()
|
|
&& !pattern.contains("//")
|
|
&& pattern.split('/').all(|seg| !seg.is_empty() || seg == "*")
|
|
}
|
|
```
|
|
|
|
#### 4.3 Wire into CLI
|
|
|
|
**File:** `applications/aphoria/src/handlers.rs`
|
|
|
|
```rust
|
|
PolicyCommand::AddAlias { pack, policy_path, target_patterns, output } => {
|
|
let signing_key = load_or_generate_key(&project_root)?;
|
|
crate::policy_ops::handle_policy_add_alias(
|
|
&pack,
|
|
policy_path,
|
|
target_patterns,
|
|
output.as_deref(),
|
|
&signing_key,
|
|
)?;
|
|
}
|
|
```
|
|
|
|
**Example Usage:**
|
|
```bash
|
|
# Security team workflow
|
|
aphoria policy export security-standards-v1.0.pack
|
|
|
|
aphoria policy add-alias \
|
|
--pack security-standards-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"
|
|
|
|
# Dev team imports and scans
|
|
aphoria scan --mode persistent
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 5: Documentation & Testing (Day 3)
|
|
|
|
#### 5.1 Update User Guide
|
|
|
|
**File:** `applications/aphoria/docs/guides/federating-truth.md`
|
|
|
|
Add section:
|
|
|
|
```markdown
|
|
### Policy Aliases
|
|
|
|
When security teams create standards using logical hierarchies (e.g., `code://standards/*`),
|
|
these may not match extractor output (e.g., `code://rust/myapp/*`).
|
|
|
|
Policy aliases bridge this gap:
|
|
|
|
```bash
|
|
# Add alias to Trust Pack
|
|
aphoria policy add-alias \
|
|
--pack security.pack \
|
|
--policy-path "code://standards/tls/cert_verification" \
|
|
--target "code://rust/*/tls/cert_verification"
|
|
```
|
|
|
|
Now scans will match code extractors against the policy path.
|
|
```
|
|
|
|
#### 5.2 Write UAT Scenario
|
|
|
|
**File:** `applications/aphoria/uat/2026-02-05-policy-alias-uat.md`
|
|
|
|
```markdown
|
|
# UAT: Policy Alias Matching
|
|
|
|
## Scenario
|
|
Security team creates standard at `code://standards/tls/cert_verification`.
|
|
Dev team code has `code://rust/myapp/tls/cert_verification`.
|
|
|
|
## Setup
|
|
1. Create security-team project with blessed assertion
|
|
2. Export Trust Pack with policy alias
|
|
3. Create dev-team project with violating code
|
|
4. Import Trust Pack
|
|
5. Scan
|
|
|
|
## Expected Outcome
|
|
- Scan detects conflict via policy alias
|
|
- Report shows policy source
|
|
- Exit code = 1 (BLOCK)
|
|
```
|
|
|
|
#### 5.3 Integration Tests
|
|
|
|
**File:** `applications/aphoria/src/tests/policy_alias_integration.rs`
|
|
|
|
```rust
|
|
#[tokio::test]
|
|
async fn test_policy_alias_matching_integration() {
|
|
// 1. Create policy assertion
|
|
let policy_assertion = create_test_assertion(
|
|
"code://standards/tls/cert_verification",
|
|
"enabled",
|
|
ObjectValue::Boolean(true),
|
|
SourceClass::Expert,
|
|
);
|
|
|
|
// 2. Create policy alias
|
|
let alias = PolicyAlias {
|
|
policy_path: "code://standards/tls/cert_verification".to_string(),
|
|
target_patterns: vec![
|
|
"code://rust/*/tls/cert_verification".to_string(),
|
|
],
|
|
};
|
|
|
|
// 3. Build Trust Pack
|
|
let key = SigningKey::generate(&mut rand::thread_rng());
|
|
let pack = TrustPack::new(
|
|
"Test Policy".to_string(),
|
|
"1.0.0".to_string(),
|
|
vec![policy_assertion],
|
|
vec![],
|
|
vec![alias],
|
|
&key,
|
|
).unwrap();
|
|
|
|
// 4. Simulate scan with code claim
|
|
let code_claim = ExtractedClaim {
|
|
concept_path: "code://rust/myapp/tls/cert_verification".to_string(),
|
|
predicate: "enabled".to_string(),
|
|
value: ObjectValue::Boolean(false), // CONFLICT
|
|
// ... other fields
|
|
};
|
|
|
|
// 5. Check conflicts
|
|
let corpus = vec![pack.assertions[0].clone()];
|
|
let index = ConceptIndex::build(&corpus);
|
|
let result = index.lookup_with_policy_aliases(
|
|
&code_claim.concept_path,
|
|
&code_claim.predicate,
|
|
&pack.policy_aliases,
|
|
);
|
|
|
|
// 6. Assert match
|
|
assert!(result.is_some(), "Policy alias should match");
|
|
assert_eq!(result.unwrap()[0].object, ObjectValue::Boolean(true));
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Migration & Rollout
|
|
|
|
### Backward Compatibility
|
|
|
|
✅ **Existing Trust Packs:**
|
|
- `policy_aliases` field is optional (deserializes as empty vec)
|
|
- No re-signing required unless adding aliases
|
|
|
|
✅ **Existing Scans:**
|
|
- Empty aliases vec = current behavior
|
|
- No performance impact (skips alias loop)
|
|
|
|
✅ **Existing CLI:**
|
|
- All existing commands work unchanged
|
|
- `policy add-alias` is additive
|
|
|
|
### Rollout Plan
|
|
|
|
**Week 1 (Dev):**
|
|
- [ ] Implement Phases 1-3
|
|
- [ ] Write unit tests
|
|
- [ ] Manual testing with UAT scenario
|
|
|
|
**Week 2 (Validation):**
|
|
- [ ] Implement Phase 4 (CLI)
|
|
- [ ] Write integration tests
|
|
- [ ] Performance benchmarks
|
|
|
|
**Week 3 (Docs & Release):**
|
|
- [ ] Update user documentation
|
|
- [ ] Write migration guide
|
|
- [ ] Release 0.2.0 with feature flag
|
|
|
|
**Week 4 (Enterprise Pilot):**
|
|
- [ ] Deploy to 2-3 enterprise teams
|
|
- [ ] Collect feedback
|
|
- [ ] Iterate on pattern syntax if needed
|
|
|
|
---
|
|
|
|
## Performance Considerations
|
|
|
|
### Lookup Complexity
|
|
|
|
**Direct tail-path:** O(1) hash lookup
|
|
**Policy alias:** O(P * A) where:
|
|
- P = patterns per alias
|
|
- A = number of aliases
|
|
|
|
**Mitigation:**
|
|
- Try direct lookup first (fast path)
|
|
- Only iterate aliases on miss
|
|
- Most scans will have < 10 aliases
|
|
- Pattern matching is simple string comparison
|
|
|
|
**Benchmark Target:** < 5% scan time increase
|
|
|
|
### Memory Overhead
|
|
|
|
**Per Trust Pack:**
|
|
- PolicyAlias: ~100 bytes
|
|
- 10 aliases: ~1 KB
|
|
- Negligible compared to corpus (MBs)
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
### 1. Recursive Wildcards
|
|
|
|
**Current:** `code://rust/*/tls` (single segment)
|
|
**Future:** `code://rust/**/tls` (any depth)
|
|
|
|
**Implementation:** Use `globset` crate for full glob support.
|
|
|
|
### 2. Regex Patterns
|
|
|
|
**Current:** Glob wildcards
|
|
**Future:** Full regex support
|
|
|
|
```rust
|
|
pub enum PatternSyntax {
|
|
Glob(String),
|
|
Regex(String),
|
|
}
|
|
```
|
|
|
|
### 3. Alias Auto-Discovery
|
|
|
|
**During Scan:** Suggest aliases when tail-path matches but full path differs.
|
|
|
|
```rust
|
|
// In conflict detection
|
|
if tail_match && !full_match {
|
|
warn!(
|
|
"Potential alias needed: {} -> {}",
|
|
claim.concept_path,
|
|
assertion.subject
|
|
);
|
|
}
|
|
```
|
|
|
|
### 4. Trust Pack Composition
|
|
|
|
**Idea:** Allow Trust Packs to "extend" other packs.
|
|
|
|
```rust
|
|
pub struct TrustPack {
|
|
pub header: PackHeader,
|
|
pub extends: Vec<String>, // URLs of parent packs
|
|
// ...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
### Functional
|
|
- [ ] Security team can create policy at `code://standards/*`
|
|
- [ ] Dev team code at `code://rust/myapp/*` matches
|
|
- [ ] Conflicts detected and reported correctly
|
|
- [ ] Trust Pack signature verifies with aliases
|
|
|
|
### Performance
|
|
- [ ] Scan time increase < 5%
|
|
- [ ] Memory overhead < 10 KB per pack
|
|
|
|
### Usability
|
|
- [ ] `policy add-alias` command works intuitively
|
|
- [ ] Trust Pack import is automatic (no manual config)
|
|
- [ ] Error messages are clear (invalid patterns, etc.)
|
|
|
|
### Quality
|
|
- [ ] 100% test coverage on pattern matching
|
|
- [ ] Integration test covers full workflow
|
|
- [ ] UAT scenario passes
|
|
|
|
---
|
|
|
|
## Questions for Review
|
|
|
|
1. **Glob Syntax:** Single wildcard (`*`) sufficient, or support recursive (`**`)?
|
|
2. **Alias Priority:** First match wins, or most specific match?
|
|
3. **Validation:** Fail Trust Pack creation if pattern is invalid?
|
|
4. **Caching:** Cache pattern match results, or recompute each time?
|
|
|
|
---
|
|
|
|
**Ready to implement.** Feedback welcome before starting Phase 1.
|