// Copyright 2026
// SPDX-License-Identifier: Apache-2.0
//
// Criterion benchmarks for secrets detection performance
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use secret_detection::{SecretsDetectionConfig, detect_and_redact};
use std::collections::HashMap;
use std::hint::black_box;
use std::time::Duration;
fn create_test_config() -> SecretsDetectionConfig {
SecretsDetectionConfig {
enabled: HashMap::from([
("aws_access_key_id".to_string(), true),
("aws_secret_access_key".to_string(), true),
("google_api_key".to_string(), true),
("slack_token".to_string(), true),
("private_key_block".to_string(), true),
("jwt_like".to_string(), true),
("hex_secret_32".to_string(), true),
("base64_24".to_string(), true),
]),
redact: true,
redaction_text: "***REDACTED***".to_string(),
block_on_detection: true,
min_findings_to_block: 1,
}
}
fn create_no_redact_config() -> SecretsDetectionConfig {
SecretsDetectionConfig {
redact: false,
..create_test_config()
}
}
// Create realistic conversation data with various secret types
fn create_realistic_conversation_data() -> Vec<(&'static str, &'static str)> {
vec![
// Clean conversation messages
(
"clean",
"I'm setting up a microservices architecture on Kubernetes. What are the best practices for service discovery?",
),
(
"clean",
"For monitoring our services, I recommend using Prometheus with Grafana dashboards and Jaeger for distributed tracing.",
),
// Messages with secrets
(
"aws_secret",
"Here are my AWS credentials: AWS_ACCESS_KEY_ID=AKIAFAKE12345EXAMPLE AWS_SECRET_ACCESS_KEY=FAKESecretAccessKeyForTestingEXAMPLE0000",
),
(
"slack_token",
"Our Slack bot token is xoxr-fake-000000000-fake000000000-fakefakefakefake for notifications",
),
(
"google_api",
"The Google API key is AIzaFAKE_KEY_FOR_TESTING_ONLY_fake12345 for our maps integration",
),
(
"jwt_token",
"JWT token: eyJfake_header_12345.eyJfake_payload_1234.fake_signature_12345678",
),
(
"hex_secret",
"Database encryption key: 00face00dead00beef00cafe00fade0000000000000000000000000000000000",
),
(
"base64_secret",
"Service account key: dGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHNlY3JldCBrZXkgZm9yIHRlc3RpbmcgcHVycG9zZXM=",
),
(
"mixed_secrets",
"Deploy with AWS_KEY=AKIAFAKE67890EXAMPLE SLACK_TOKEN=xoxr-fake-123-456-789 API_KEY=AIzaFAKE_KEY_FOR_TESTING_ONLY_fake12345",
),
]
}
// Benchmark detect_and_redact function with realistic conversation data
fn bench_detect_and_redact(c: &mut Criterion) {
let messages = create_realistic_conversation_data();
let config = create_test_config();
let no_redact_config = create_no_redact_config();
let mut group = c.benchmark_group("detect_and_redact");
group.measurement_time(Duration::from_millis(500));
group.warm_up_time(Duration::from_millis(100));
group.sample_size(50);
for (message_type, message) in messages.iter() {
group.throughput(Throughput::Bytes(message.len() as u64));
// With redaction
group.bench_with_input(
BenchmarkId::new("with_redaction", message_type),
message,
|b, msg| {
b.iter(|| detect_and_redact(black_box(msg), black_box(&config)));
},
);
// Detection only (no redaction)
group.bench_with_input(
BenchmarkId::new("detection_only", message_type),
message,
|b, msg| {
b.iter(|| detect_and_redact(black_box(msg), black_box(&no_redact_config)));
},
);
}
group.finish();
}
// Benchmark batch processing
fn bench_batch_processing(c: &mut Criterion) {
let messages = create_realistic_conversation_data();
let config = create_test_config();
let mut group = c.benchmark_group("batch_processing");
group.measurement_time(Duration::from_millis(500));
group.warm_up_time(Duration::from_millis(100));
group.sample_size(50);
// Extract just the messages for batch processing
let message_texts: Vec<&str> = messages.iter().map(|(_, msg)| *msg).collect();
let total_bytes: u64 = message_texts.iter().map(|m| m.len() as u64).sum();
group.throughput(Throughput::Bytes(total_bytes));
group.bench_function("all_messages_batch", |b| {
b.iter(|| {
for message in &message_texts {
let _ = detect_and_redact(black_box(message), black_box(&config));
}
});
});
// Test individual messages with secrets
let secret_messages: Vec<&str> = messages
.iter()
.filter(|(msg_type, _)| *msg_type != "clean")
.map(|(_, msg)| *msg)
.collect();
for (i, message) in secret_messages.iter().enumerate() {
group.throughput(Throughput::Bytes(message.len() as u64));
group.bench_with_input(
BenchmarkId::new("individual_with_secrets", i),
message,
|b, msg| {
b.iter(|| detect_and_redact(black_box(msg), black_box(&config)));
},
);
}
group.finish();
}
criterion_group!(benches, bench_detect_and_redact, bench_batch_processing);
criterion_main!(benches);