# Generic API-key bridge (phase-2). A shapeless API key, a high-entropy token
# assigned to an api_key/access_key/secret_key field, has no vendor prefix to
# anchor a regex on, so it fires in phase 2, gated by its keywords and entropy
# policy. Optional JSON envelopes below preserve structured low-entropy values.
# Every knob lives HERE (Tier-B), not in engine constants: this is the single
# home for the generic-api-key detector.
[detector]
id = "generic-api-key"
name = "Generic API Key"
service = "generic"
entropy_roles = ["unclaimed-keyword"]
severity = "medium"
ml = { match_mode = "lift", entropy_mode = "authoritative", weight = 1.0, context_radius_lines = 5 }
match_confidence = { literal_prefix_weight = 0.35, context_anchor_weight = 0.20, entropy_weight = 0.20, high_entropy_partial_weight = 0.12, moderate_entropy_threshold = 3.0, moderate_entropy_weight = 0.05, low_entropy_penalty_floor = 2.0, low_entropy_min_match_length = 10, low_entropy_penalty_multiplier = 0.60, keyword_nearby_weight = 0.10, sensitive_file_weight = 0.10, companion_weight = 0.05, very_high_entropy_margin = 1.2999999999999998, low_promise_confidence = 0.10, assignment_context_multiplier = 1.0, string_literal_context_multiplier = 0.9, unknown_context_multiplier = 0.8, documentation_context_multiplier = 0.3, comment_context_multiplier = 0.4, test_context_multiplier = 0.3, encrypted_context_multiplier = 0.05, soft_context_suppression_threshold = 0.5, encrypted_context_suppression_threshold = 0.8, post_match = { placeholder_multiplier = 0.05, minimum_byte_diversity = 0.3, low_diversity_multiplier = 0.1, maximum_repeat_ratio = 0.5, degenerate_run_min_length = 10, degenerate_repeat_multiplier = 0.1, data_envelope_multiplier = 0.02, fixture_path_multiplier = 0.5, ml_context_reapply_below = 0.95 } }
kind = "phase2-generic"
keywords = ["api_key", "apikey", "api-key", "access_key", "accesskey", "secret_key", "secretkey", "client_secret", "x-api-key", "apitoken", "api_token", "auth_key", "signing_key", "encryption_key", "master_key", "masterkey", "session_key", "hmac_secret", "hmac_seed", "license_key"]
entropy_high = 4.5
entropy_low = 3.0
entropy_very_high = 5.8
# Map this detector's entropy evidence to report confidence without scanner defaults.
entropy_fallback_confidence = { low_entropy_max = 0.55, high_entropy = 0.65, very_high_entropy = 0.75, keyword_lift = 0.1, max_confidence = 0.9 }
sensitive_path_entropy_very_high = 5.8
plausibility = { mixed_alnum_floor = 4.0, symbolic_entropy_floor = 3.5, second_half_entropy_floor = 2.5, second_half_min_len = 17, unique_chars_min_len = 17, min_unique_chars = 8, unanchored_hex_max_len = 10, identical_char_max_len = 4, structured_dotted_min_len = 40, mixed_alnum_min_len = 20, isolated_mixed_entropy_floor = 3.65, isolated_symbolic_min_len = 18, isolated_symbolic_min_symbols = 2, isolated_symbolic_requires_non_underscore = true, isolated_alpha_only_min_symbols = 3, isolated_alpha_only_min_alpha_ratio = 0.5, min_alnum_ratio = 0.5, source_type_name_max_len = 40, source_type_name_min_uppercase = 2, url_path_high_entropy_min_len = 41, isolated_colon_left_min_len = 20, isolated_colon_right_min_len = 16, leading_slash_base64_entropy_floor = 4.8, leading_slash_base64_min_len = 40, reject_repeated_blocks = true, allow_alphabetic_credential = true, reject_program_identifiers = true, reject_source_symbol_identifiers = true, reject_dash_segmented_alnum = true }
# Specific API/key-material policy outranks broader generic secret bridges.
entropy_policy_priority = 80
bpe_enabled = true
# The CredData lowercase-random fixture `ufnlbbavawsdeecn` measures 2.286
# bytes/token under cl100k_base. A 2.3 ceiling admits it while remaining well
# below the shipped word-like FP taxonomy (3.8+ bytes/token).
bpe_max_bytes_per_token = 2.3
# API/encryption-key assignments may carry base64-wrapped 32- or 48-character
# hex keys. SHA-1/SHA-256 widths remain decoded-digest negatives.
decoded_hex_key_material_lengths = [32, 48]
# Canonical pure-hex values are ambiguous with digests, so the owning detector
# declares exact keyword/length pairs. Broad API-key slots admit 128/192-bit
# material; only explicit cryptographic-key slots admit SHA-256-shaped 64-hex.
canonical_hex_key_material = [
{ lengths = [32, 48], keywords = ["api_key", "access_key", "secret_key", "client_secret", "x-api-key", "auth_key", "signing_key", "encryption_key", "master_key", "session_key", "hmac_secret", "hmac_seed"], suffixes = ["key", "secret"], excluded_keywords = ["license_key"] },
{ lengths = [64], keywords = ["signing_key", "encryption_key", "master_key", "session_key", "hmac_secret", "hmac_seed"] },
]
min_len = 8
# API/access keys are bounded tokens. Longer assignment payloads belong to a
# structured/vendor detector and must not be truncated into a generic key.
max_len = 512
keyword_free_min_len = 20
stopwords = ["example", "sample", "test", "dummy", "placeholder", "changeme", "xxxxxxxx", "your_key_here", "000000"]
public_identifier_assignment_markers = [
"_ADDR=", "_ADDR ", "_ADDR\"", "_ADDR:",
"_ADDRS=", "_ADDRS ", "_ADDRS\"",
"_ADDRESS=", "_ADDRESS ", "_ADDRESS\"",
"_WALLET=", "_WALLET ", "_WALLET\"", "_MINT_ADDR",
"_PUBKEY=", "_PUBKEY ", "_PUBLIC_KEY=", "_PUBLIC_KEY ", "_PUBLIC_KEY\"",
"_CONTRACT=", "_CONTRACT ", "_OWNER=", "_ACCOUNT_ID=", "_PEER_ID=", "_NODE_ID=",
]
# Length-specific low-entropy floors. These measured buckets are deliberately
# nonmonotonic: 25 to 40 bytes use 2.8, values through 24 use 3.0, and values
# above 40 use 3.5. The first bucket whose max_len >= L applies.
entropy_floor = [
{ max_len = 24, floor = 3.0 },
{ max_len = 40, floor = 2.8 },
{ floor = 3.5 },
]
[[detector.entropy_shapes]]
charset = "lower-alnum"
entropy_floor = 3.9
special_min_length = 16
grouping = { group_count = 4, group_length = 4, separator = "-" }
require_non_hex_alpha = true
require_group_alpha_digit = true
[detector.generic_assignment_confidence]
ordinary_base = 0.60
test_base = 0.25
documentation_base = 0.30
comment_base = 0.30
scanned_comment_base = 0.60
entropy_reference = 3.5
entropy_gain_per_bit = 0.10
entropy_lift_max = 0.25
length_reference = 16
length_gain_per_byte = 0.005
length_lift_max = 0.15
max_confidence = 0.95
[detector.entropy_fallback]
class = "api-key"
id = "entropy-api-key"
name = "API Key (Entropy Detected)"
service = "generic"
# Explicit JSON envelopes belong to the API/access-key owner. Keeping these as
# separate fixed-anchor patterns makes every backend compile the same trigger
# roots instead of relying on one multi-family alternation.
[[detector.patterns]]
regex = '(?i)"api[_\-\s]*key"\s*:\s*"([a-zA-Z0-9/+=_.!@#$%^&*-]{12,80})"'
description = "API key value in a JSON object field"
group = 1
[[detector.patterns]]
regex = '(?i)"access[_\-\s]*key"\s*:\s*"([a-zA-Z0-9/+=_.!@#$%^&*-]{12,80})"'
description = "Access key value in a JSON object field"
group = 1
[[detector.tests]]
test_positive = "api_key=ufnlbbavawsdeecn"
test_negative = "api_key=example"