Reduces scan noise by 96% through proper exclusion of test fixtures, demo apps, and intentional vulnerabilities. Phase 16.1 - Glob Pattern Matching: - Replace starts_with() with globset for ** and * patterns - Backwards compatible with legacy prefix patterns - Add walker/mod.rs tests for glob exclusions Phase 16.2 - .aphoriaignore File: - Create walker/ignore_file.rs for gitignore-style parsing - Merge with aphoria.toml excludes - Support # comments and whitespace trimming Phase 16.3 - Inline Ignore Comments: - Create extractors/ignore_comments.rs parser - Support // aphoria:ignore, // aphoria:ignore-next-line - Support // aphoria:ignore-block / // aphoria:end-ignore - Multiple comment styles: //, #, /*, --, <!-- - Integrate with ExtractorRegistry.extract_all() Phase 16.4 - Ack Export/Import: - Create ack_file.rs for TOML serialization - Add 'aphoria ack add' subcommand - Add 'aphoria ack export' to .aphoria/acks.toml - Add 'aphoria ack import' from .aphoria/acks.toml - Preserve expiry and reason fields Also configures stemedb with: - aphoria.toml with glob excludes for vulnbank, extractors, fixtures - .aphoriaignore for dashboard, community, latent, SDK examples Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
169 lines
4.5 KiB
Rust
169 lines
4.5 KiB
Rust
//! Policy operation handlers (ack, bless, update, baseline, diff, status, init)
|
|
|
|
use std::process::ExitCode;
|
|
|
|
use aphoria::{AcknowledgeArgs, AphoriaConfig, BlessArgs, UpdateArgs};
|
|
|
|
use crate::cli::AckCommands;
|
|
|
|
/// Handle the ack command and its subcommands.
|
|
pub async fn handle_ack_command(command: AckCommands, config: &AphoriaConfig) -> ExitCode {
|
|
match command {
|
|
AckCommands::Add { concept_path, reason, expires } => {
|
|
handle_ack_add(concept_path, reason, expires, config).await
|
|
}
|
|
AckCommands::Export { output } => handle_ack_export(output, config).await,
|
|
AckCommands::Import { input } => handle_ack_import(input, config).await,
|
|
}
|
|
}
|
|
|
|
async fn handle_ack_add(
|
|
concept_path: String,
|
|
reason: String,
|
|
expires: Option<String>,
|
|
config: &AphoriaConfig,
|
|
) -> ExitCode {
|
|
let args = AcknowledgeArgs { concept_path, reason, expires: expires.clone() };
|
|
|
|
match aphoria::acknowledge(args, config).await {
|
|
Ok(()) => {
|
|
if let Some(exp) = expires {
|
|
println!("Conflict acknowledged (expires {exp}).");
|
|
} else {
|
|
println!("Conflict acknowledged.");
|
|
}
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Acknowledge error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn handle_ack_export(output: Option<std::path::PathBuf>, config: &AphoriaConfig) -> ExitCode {
|
|
match aphoria::export_acks(output, config).await {
|
|
Ok(stats) => {
|
|
println!(
|
|
"Exported {} acknowledgments to {}",
|
|
stats.exported,
|
|
stats.output_path.display()
|
|
);
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Export error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn handle_ack_import(input: Option<std::path::PathBuf>, config: &AphoriaConfig) -> ExitCode {
|
|
match aphoria::import_acks(input, config).await {
|
|
Ok(stats) => {
|
|
println!(
|
|
"Imported {} acknowledgments ({} skipped as duplicates)",
|
|
stats.imported, stats.skipped
|
|
);
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Import error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn handle_bless(
|
|
concept_path: String,
|
|
predicate: String,
|
|
value: String,
|
|
reason: String,
|
|
config: &AphoriaConfig,
|
|
) -> ExitCode {
|
|
let args = BlessArgs { concept_path, predicate, value, reason };
|
|
|
|
match aphoria::bless(args, config).await {
|
|
Ok(()) => {
|
|
println!("Pattern blessed as authoritative standard.");
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Bless error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn handle_update(
|
|
concept_path: String,
|
|
value: String,
|
|
reason: String,
|
|
config: &AphoriaConfig,
|
|
) -> ExitCode {
|
|
let args = UpdateArgs { concept_path, value, reason };
|
|
|
|
match aphoria::update(args, config).await {
|
|
Ok(()) => {
|
|
println!("Policy update recorded.");
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Update error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn handle_baseline(config: &AphoriaConfig) -> ExitCode {
|
|
match aphoria::set_baseline(config).await {
|
|
Ok(()) => {
|
|
println!("Baseline set.");
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Baseline error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn handle_diff(config: &AphoriaConfig) -> ExitCode {
|
|
match aphoria::show_diff(config).await {
|
|
Ok(output) => {
|
|
println!("{output}");
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Diff error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn handle_status(config: &AphoriaConfig) -> ExitCode {
|
|
match aphoria::show_status(config).await {
|
|
Ok(output) => {
|
|
println!("{output}");
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Status error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn handle_init(config: &AphoriaConfig) -> ExitCode {
|
|
match aphoria::initialize(config).await {
|
|
Ok(()) => {
|
|
println!("Aphoria initialized. Run `aphoria scan <project>` to begin.");
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Init error: {e}");
|
|
ExitCode::from(3)
|
|
}
|
|
}
|
|
}
|