Files
2026-09-30 20:30:56 +03:00

2466 lines
101 KiB
Python

import hashlib
import json
import os
import re
import shutil
import sqlite3
import sys
import time
import uuid
from collections import OrderedDict
from contextlib import contextmanager
from datetime import datetime, timezone
from itertools import cycle
import requests
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if PROJECT_DIR not in sys.path:
sys.path.append(PROJECT_DIR)
from paths import apply_path_config, default_project_paths
from scanner_db import ScannerDB, extract_raw_secret, json_dumps, safe_json_loads, sha256_text
from lifecycle_authority import LifecycleAuthorityError, require_active_supervisor_child
from runtime_security import (
canonical_path,
durable_replace,
durable_unlink,
PrivateFileLock,
harden_private_file,
read_private_json,
reject_reparse_components,
require_private_directory,
require_private_file,
)
STATUS_TRANSACTION_JOURNAL_FILENAME = ".status-transaction.pending.json"
STATUS_TRANSACTION_LOCK_FILENAME = ".status-transaction.lock"
STATUS_TRANSACTION_MAX_KEY_BYTES = 8192
STATUS_TRANSACTION_MAX_LINE_BYTES = 8192
STATUS_TRANSACTION_MAX_JOURNAL_BYTES = 64 * 1024
GCP_STATUS_MAX_LINE_BYTES = 256 * 1024
GCP_STATUS_MAX_JOURNAL_BYTES = 1024 * 1024
GENERIC_SK_PROVIDERS = frozenset(("qwen", "deepseek", "kimi", "zai"))
QWEN_DEEPSEEK_PROVIDERS = frozenset(("qwen", "deepseek"))
AMBIGUOUS_QWEN_DEEPSEEK_HINT = "ambiguous_qwen_deepseek"
AMBIGUOUS_GENERIC_SK_HINT = "ambiguous_generic_sk"
EXPLICIT_ASSIGNMENT_PROVIDER_HINT_SOURCE = "explicit_assignment"
QWEN_EXPLICIT_ROUTING_DETECTORS = frozenset(("qwendashscope", "qwen_dashscope"))
DEEPSEEK_EXPLICIT_ROUTING_DETECTORS = frozenset(("deepseekapikey", "deepseek_api_key"))
KIMI_EXPLICIT_ROUTING_DETECTORS = frozenset(("kimimoonshot", "moonshotai"))
ZAI_EXPLICIT_ROUTING_DETECTORS = frozenset(("zaiglm",))
GENERIC_SK_NON_EXPLICIT_ROUTING_DETECTORS = frozenset(("qwen", "dashscope", "deepseek"))
PROVIDER_ROUTING_EVIDENCE_CACHE = OrderedDict()
PROVIDER_ROUTING_EVIDENCE_CACHE_AUTHORITY = None
PROVIDER_ROUTING_EVIDENCE_DB_FAILED = False
REVIEWED_CORRUPTION_MAX_ROWS = 100000
KEYCHECK_CAPACITY_BLOCKED_EXIT = 76
REVIEWED_CORRUPTION_LEDGER_MAX_BYTES = 512 * 1024 * 1024
REVIEWED_CORRUPTION_CLASSIFICATIONS = frozenset((
'invalid_json', 'legacy_numeric_prefix_corrupt_json', 'invalid_utf8',
'utf8_bom_prefix', 'unterminated_record', 'invalid_error_projection',
'oversized_record',
))
_POSTGRES_DB = None
_ACTIVE_DB_CANDIDATE = None
def keycheck_input_mode():
default = 'postgres' if os.getenv('SCANNER_SUPERVISED') == '1' else 'jsonl'
return str(os.getenv('KEYCHECK_INPUT_MODE') or default).strip().lower()
def _postgres_candidate_db():
global _POSTGRES_DB
if _POSTGRES_DB is not None and _POSTGRES_DB.enabled:
return _POSTGRES_DB
db_url = str(os.getenv('KEYCHECK_DB_URL') or os.getenv('SCANNER_DB_URL') or '').strip()
if not db_url:
raise RuntimeError('PostgreSQL keycheck input mode requires KEYCHECK_DB_URL')
_POSTGRES_DB = ScannerDB(db_url=db_url, initialize=False)
if not _POSTGRES_DB.enabled:
raise RuntimeError('PostgreSQL keycheck candidate database is unavailable')
_POSTGRES_DB.set_application_name(f'truf-keycheck-provider:{keycheck_service_name() or "unknown"}')
_POSTGRES_DB.require_runtime_safety_schema()
_POSTGRES_DB.require_final_cutover()
return _POSTGRES_DB
def now_iso():
return datetime.now(timezone.utc).isoformat(timespec="seconds")
def env_int(name, default):
try:
return int(os.getenv(name, default))
except (TypeError, ValueError):
return default
def require_provider_authority(service):
"""Authenticate a leaf checker before it parses arguments or opens findings."""
metadata = require_active_supervisor_child(
child_kind='keycheck-provider',
require_dsn=True,
handshake_timeout_retries=1,
)
expected_service = str(service or '').strip().lower()
if not expected_service or str(os.getenv('KEYCHECK_SERVICE') or '').strip().lower() != expected_service:
raise LifecycleAuthorityError('keycheck provider capability does not match this service')
canonical_dsn = os.getenv('TRUF_MANAGED_POSTGRES_DSN') or ''
if os.getenv('KEYCHECK_DB_URL') != canonical_dsn:
raise LifecycleAuthorityError('keycheck provider database URL is not supervisor-managed')
if keycheck_input_mode() == 'postgres' and any(
str(value).lower() in ('--input', '--plain')
or str(value).lower().startswith(('--input=', '--plain='))
for value in sys.argv[1:]
):
raise LifecycleAuthorityError(
'PostgreSQL keycheck providers forbid compatibility input/plain arguments'
)
try:
import yaml
with open(metadata['config_path'], 'r', encoding='utf-8') as handle:
config = apply_path_config(yaml.safe_load(handle) or {}, metadata['config_path'])
expected_output = os.path.join((config.get('global') or {})['keycheck_dir'], expected_service)
except Exception as exc:
raise LifecycleAuthorityError('keycheck provider could not validate its canonical output path') from exc
output_dir = os.getenv('KEYCHECK_OUTPUT_DIR') or ''
state_dir = os.getenv('KEYCHECK_STATE_DIR') or ''
if canonical_path(output_dir) != canonical_path(expected_output) or canonical_path(state_dir) != canonical_path(expected_output):
raise LifecycleAuthorityError('keycheck provider output capability is outside its canonical service directory')
return metadata
def load_keycheck_layout(config_path=None):
if config_path:
try:
import yaml
with open(config_path, "r", encoding="utf-8") as f:
config = apply_path_config(yaml.safe_load(f) or {}, config_path)
return config.get("global") or {}
except Exception:
pass
return default_project_paths()
def service_output_dir(service, config_path=None, output_dir=None):
if output_dir:
return output_dir
layout = load_keycheck_layout(config_path)
return os.path.join(layout["keycheck_dir"], service)
def default_input_file(config_path=None):
layout = load_keycheck_layout(config_path)
return os.path.join(layout["results_dir"], "found_secrets.jsonl")
def default_proxy_file(config_path=None):
layout = load_keycheck_layout(config_path)
return layout["proxy_file"]
def truthy_env(name, default=False):
value = os.getenv(name)
if value is None:
return default
return str(value).strip().lower() in ("1", "true", "yes", "on")
def keycheck_service_name():
service = os.getenv("KEYCHECK_SERVICE")
if service:
return service.strip().lower()
output_dir = os.getenv("KEYCHECK_OUTPUT_DIR")
if output_dir:
return os.path.basename(os.path.normpath(output_dir)).lower()
return ""
def status_projection_line_max_bytes():
if os.getenv("KEYCHECK_INPUT_LIST_MAX_LINE_BYTES") is not None:
return max(1, env_int("KEYCHECK_INPUT_LIST_MAX_LINE_BYTES", STATUS_TRANSACTION_MAX_LINE_BYTES))
if keycheck_service_name() == "gcp":
return GCP_STATUS_MAX_LINE_BYTES
return STATUS_TRANSACTION_MAX_LINE_BYTES
def status_transaction_journal_max_bytes():
if keycheck_service_name() == "gcp":
return GCP_STATUS_MAX_JOURNAL_BYTES
return STATUS_TRANSACTION_MAX_JOURNAL_BYTES
def keycheck_state_path(service=None):
service = service or keycheck_service_name() or "default"
state_dir = os.getenv("KEYCHECK_STATE_DIR")
if not state_dir:
output_dir = os.getenv("KEYCHECK_OUTPUT_DIR")
state_dir = output_dir or service_output_dir(service)
require_private_directory(state_dir, create=True)
return os.path.join(state_dir, "input_state.json")
def read_json_file(path, default=None):
try:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
except (OSError, ValueError):
return default
def write_json_file(path, data):
with private_atomic_writer(path) as f:
json.dump(data, f, ensure_ascii=False, indent=2, sort_keys=True)
def input_file_signature(path, handle=None):
details = os.fstat(handle.fileno()) if handle is not None else os.stat(path)
return {
"path": os.path.abspath(path),
"device": int(getattr(details, "st_dev", 0) or 0),
"size": int(details.st_size),
"mtime_ns": int(getattr(details, "st_mtime_ns", int(details.st_mtime * 1_000_000_000))),
"inode": int(getattr(details, "st_ino", 0) or 0),
}
def jsonl_manifest_path(path):
base, _ = os.path.splitext(path)
return f"{base}.manifest.json"
def load_jsonl_manifest(path):
try:
if os.path.getsize(jsonl_manifest_path(path)) > 1024 * 1024:
raise RuntimeError(f"keycheck JSONL manifest exceeds bounded size: {jsonl_manifest_path(path)}")
with open(jsonl_manifest_path(path), "r", encoding="utf-8") as f:
data = json.load(f)
return data if isinstance(data, dict) else {}
except FileNotFoundError:
return {}
def write_jsonl_manifest(path, manifest):
manifest_path = jsonl_manifest_path(path)
with private_atomic_writer(manifest_path) as f:
json.dump(manifest, f, ensure_ascii=False, indent=2, sort_keys=True)
@contextmanager
def private_atomic_writer(path, binary=False, suffix=".tmp"):
"""Publish a same-directory file whose ACL is exact-private before payload writes."""
path = os.path.abspath(path)
require_private_directory(os.path.dirname(path), create=True)
if os.path.lexists(path):
require_private_file(path)
temporary = f"{path}.{os.getpid()}.{uuid.uuid4().hex}{suffix}"
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
if hasattr(os, "O_BINARY"):
flags |= os.O_BINARY
descriptor = os.open(temporary, flags, 0o600)
published = False
try:
harden_private_file(temporary)
require_private_file(temporary)
mode = "wb" if binary else "w"
options = {} if binary else {"encoding": "utf-8", "newline": ""}
with os.fdopen(descriptor, mode, **options) as handle:
descriptor = None
yield handle
handle.flush()
os.fsync(handle.fileno())
require_private_file(temporary)
durable_replace(temporary, path)
published = True
require_private_file(path)
finally:
if descriptor is not None:
os.close(descriptor)
if not published and os.path.exists(temporary):
os.remove(temporary)
@contextmanager
def private_append_writer(path, binary=False):
"""Open an exact-private output for append, rejecting unsafe existing files."""
path = os.path.abspath(path)
ensure_output_files([path])
require_private_file(path)
mode = "ab" if binary else "a"
options = {} if binary else {"encoding": "utf-8", "newline": ""}
with open(path, mode, **options) as handle:
yield handle
handle.flush()
os.fsync(handle.fileno())
require_private_file(path)
def acquire_file_lock(lock_path, stale_sec=300, timeout_sec=30):
require_private_directory(os.path.dirname(os.path.abspath(lock_path)), create=True)
reject_reparse_components(os.path.dirname(os.path.abspath(lock_path)))
deadline = time.monotonic() + max(1, timeout_sec)
while True:
lock = PrivateFileLock(lock_path)
try:
return lock.acquire()
except BlockingIOError:
if time.monotonic() >= deadline:
raise TimeoutError(f"timed out acquiring lock {lock_path}")
time.sleep(0.05)
def release_file_lock(lock, lock_path):
try:
lock.release()
except (AttributeError, OSError):
return
def next_jsonl_segment_path(path, manifest):
base, ext = os.path.splitext(path)
seq = int(manifest.get("next_sequence") or 1)
while True:
segment = f"{base}.{seq:06d}{ext or '.jsonl'}"
if not os.path.exists(segment):
return segment, seq
seq += 1
def physical_jsonl_segments(path):
root = os.path.dirname(os.path.abspath(path))
base, extension = os.path.splitext(os.path.basename(path))
pattern = re.compile(rf"^{re.escape(base)}\.(\d{{6}}){re.escape(extension)}$")
output = []
inspect_limit = max(2, env_int("KEYCHECK_RESULTS_MAX_SEGMENTS", 16) + 1)
try:
with os.scandir(root) as entries:
for entry in entries:
match = pattern.fullmatch(entry.name)
if not match:
continue
if entry.is_symlink() or not entry.is_file(follow_symlinks=False):
raise RuntimeError(f"unsafe keycheck JSONL segment: {entry.path}")
output.append((int(match.group(1)), os.path.abspath(entry.path)))
if len(output) > inspect_limit:
raise RuntimeError(f"keycheck JSONL segment count exceeds its bound: {path}")
except FileNotFoundError:
return []
return sorted(output)
def _keycheck_files_share_prefix(segment_path, current_path, size):
if size <= 0 or not os.path.isfile(current_path) or os.path.getsize(current_path) < size:
return False
remaining = size
with open(segment_path, "rb") as segment, open(current_path, "rb") as current:
while remaining:
amount = min(1024 * 1024, remaining)
left = segment.read(amount)
right = current.read(amount)
if not left or left != right:
return False
remaining -= len(left)
return remaining == 0
def _publish_empty_keycheck_generation(path):
with private_atomic_writer(path, binary=True, suffix=".empty.tmp"):
pass
def _keycheck_remove_prefix(path, size):
current_size = os.path.getsize(path)
if current_size == size:
_publish_empty_keycheck_generation(path)
return
require_private_file(path)
with open(path, "rb") as source, private_atomic_writer(path, binary=True, suffix=".prefix.tmp") as destination:
source.seek(size)
shutil.copyfileobj(source, destination, 1024 * 1024)
def _keycheck_file_generation(path):
details = os.stat(path, follow_symlinks=False)
return {
"device": int(getattr(details, "st_dev", 0) or 0),
"size": int(details.st_size),
"mtime_ns": int(getattr(details, "st_mtime_ns", int(details.st_mtime * 1_000_000_000))),
"inode": int(getattr(details, "st_ino", 0) or 0),
}
def _keycheck_manifest_skip_valid(path, manifest, skip):
signature = manifest.get("current_skip_signature") if isinstance(manifest, dict) else None
if not isinstance(signature, dict) or not os.path.isfile(path):
return False
try:
current = _keycheck_file_generation(path)
return int(skip) <= current["size"] and all(
int(current[key]) == int(signature.get(key, -1))
for key in ("device", "size", "mtime_ns", "inode")
)
except (OSError, TypeError, ValueError):
return False
def repair_keycheck_jsonl_tail(path):
if not os.path.exists(path) or os.path.getsize(path) == 0:
return 0
require_private_file(path)
scan_limit = max(1, env_int("KEYCHECK_JSONL_TAIL_SCAN_MAX_BYTES", 8 * 1024 * 1024))
quarantine_limit = max(1, env_int("KEYCHECK_JSONL_TORN_QUARANTINE_MAX_BYTES", 64 * 1024))
size = os.path.getsize(path)
with open(path, "r+b") as handle:
handle.seek(-1, os.SEEK_END)
if handle.read(1) == b"\n":
return 0
start = max(0, size - scan_limit)
handle.seek(start)
tail = handle.read(size - start)
newline = tail.rfind(b"\n")
if newline < 0 and start:
raise RuntimeError(f"keycheck JSONL tail exceeds bounded repair window: {path}")
truncate_at = start + newline + 1 if newline >= 0 else 0
torn = tail[newline + 1:] if newline >= 0 else tail
quarantine = f"{path}.torn-tail.bin"
with private_atomic_writer(quarantine, binary=True) as output:
output.write(torn[:quarantine_limit])
handle.truncate(truncate_at)
handle.flush()
os.fsync(handle.fileno())
return size - truncate_at
def reconcile_keycheck_jsonl_segments(path):
manifest = load_jsonl_manifest(path)
physical = physical_jsonl_segments(path)
listed = {
str(item.get("name") or os.path.basename(str(item.get("path") or ""))): item
for item in (manifest.get("segments") or []) if isinstance(item, dict)
}
segments = []
missing = []
for sequence, segment_path in physical:
item = listed.get(os.path.basename(segment_path))
if item is None:
item = {
"name": os.path.basename(segment_path), "path": segment_path,
"bytes": os.path.getsize(segment_path), "closed_at": now_iso(),
"sequence": sequence,
}
missing.append(segment_path)
else:
item = dict(item, name=os.path.basename(segment_path), path=segment_path, sequence=sequence)
segments.append(item)
skip = max(0, int(manifest.get("current_skip_bytes") or 0))
duplicate = None
for segment_path in list(reversed(missing)) + ([physical[-1][1]] if skip and physical else []):
size = os.path.getsize(segment_path)
if _keycheck_files_share_prefix(segment_path, path, size):
duplicate = (segment_path, size)
break
effective_skip = duplicate[1] if duplicate else 0
if missing or segments != (manifest.get("segments") or []) or skip or manifest.get("current_skip_signature"):
manifest.update({
"current": os.path.basename(path), "current_path": os.path.abspath(path),
"next_sequence": max([item[0] for item in physical] or [0]) + 1,
"segments": segments,
"current_skip_bytes": effective_skip,
"current_skip_signature": _keycheck_file_generation(path) if effective_skip and os.path.isfile(path) else None,
"updated_at": now_iso(),
})
write_jsonl_manifest(path, manifest)
if duplicate:
_keycheck_remove_prefix(path, duplicate[1])
manifest["current_skip_bytes"] = 0
manifest["current_skip_signature"] = None
manifest["updated_at"] = now_iso()
write_jsonl_manifest(path, manifest)
return manifest
def rotate_jsonl_if_needed(path, max_bytes):
if not max_bytes or max_bytes <= 0 or not os.path.lexists(path):
return
reject_reparse_components(path)
repair_keycheck_jsonl_tail(path)
size = os.stat(path, follow_symlinks=False).st_size
if size < max_bytes:
return
manifest = reconcile_keycheck_jsonl_segments(path)
max_segments = max(1, env_int("KEYCHECK_RESULTS_MAX_SEGMENTS", 16))
if len(physical_jsonl_segments(path)) >= max_segments:
raise RuntimeError(
f"keycheck JSONL segment bound reached for {path}; ingest and retire segments offline"
)
segment_path, seq = next_jsonl_segment_path(path, manifest)
require_private_file(path)
with open(path, "rb") as source, private_atomic_writer(segment_path, binary=True) as destination:
shutil.copyfileobj(source, destination, 1024 * 1024)
segments = manifest.get("segments") if isinstance(manifest.get("segments"), list) else []
segments.append({
"name": os.path.basename(segment_path),
"path": segment_path,
"bytes": int(size),
"closed_at": now_iso(),
"sequence": seq,
})
manifest.update({
"current": os.path.basename(path),
"current_path": path,
"next_sequence": seq + 1,
"max_bytes": int(max_bytes),
"segments": segments,
"current_skip_bytes": int(size),
"current_skip_signature": _keycheck_file_generation(path),
"updated_at": now_iso(),
})
write_jsonl_manifest(path, manifest)
_publish_empty_keycheck_generation(path)
manifest["current_skip_bytes"] = 0
manifest["current_skip_signature"] = None
manifest["updated_at"] = now_iso()
write_jsonl_manifest(path, manifest)
def should_rotate_jsonl(path):
return os.path.basename(str(path or "")).lower().endswith("results.jsonl")
def tail_fallback_bytes():
tail_mb = os.getenv("KEYCHECK_INPUT_TAIL_MB") or os.getenv("KEYCHECK_TAIL_MB")
try:
return int(float(tail_mb) * 1024 * 1024) if tail_mb else int(os.getenv("KEYCHECK_INPUT_TAIL_BYTES", "0") or 0)
except (TypeError, ValueError):
return 0
def keycheck_input_max_line_bytes():
return max(1024, env_int("KEYCHECK_INPUT_MAX_LINE_BYTES", 16 * 1024 * 1024))
def keycheck_reconciliation_ledger_path(input_file):
base, _ = os.path.splitext(os.path.abspath(input_file))
return base + '.publication-ledger.sqlite3'
def load_reviewed_keycheck_corruptions(input_file):
ledger_path = keycheck_reconciliation_ledger_path(input_file)
if not os.path.lexists(ledger_path):
return {}
require_private_file(ledger_path)
max_bytes = max(
1024 * 1024,
env_int('KEYCHECK_RECONCILIATION_LEDGER_MAX_BYTES', REVIEWED_CORRUPTION_LEDGER_MAX_BYTES),
)
if os.path.getsize(ledger_path) > max_bytes:
raise RuntimeError('keycheck reconciliation ledger exceeds its byte bound')
uri = 'file:' + ledger_path.replace('\\', '/') + '?mode=ro'
connection = sqlite3.connect(uri, uri=True, timeout=5)
connection.row_factory = sqlite3.Row
try:
connection.execute('PRAGMA query_only=ON')
connection.execute('PRAGMA busy_timeout=5000')
columns = {
row['name'] for row in connection.execute('PRAGMA table_info(reconciliation_issue)').fetchall()
}
required = {
'identity_key', 'file_name', 'file_device', 'file_inode', 'file_size',
'file_mtime_ns', 'byte_offset', 'byte_length', 'record_sha256',
'classification', 'status', 'resolved_at',
}
if not required.issubset(columns):
raise RuntimeError('keycheck reconciliation ledger schema is incomplete')
marker = connection.execute(
'SELECT value FROM publication_meta WHERE key = ?', ('bootstrapped:finding_uid',),
).fetchone()
if not marker or str(marker['value'] or '') != '1':
raise RuntimeError('keycheck reconciliation ledger is not complete')
max_rows = min(
REVIEWED_CORRUPTION_MAX_ROWS,
max(1, env_int('KEYCHECK_REVIEWED_CORRUPTION_MAX_ROWS', REVIEWED_CORRUPTION_MAX_ROWS)),
)
rows = connection.execute(
'''SELECT file_name, file_device, file_inode, file_size, file_mtime_ns,
byte_offset, byte_length, record_sha256, classification
FROM reconciliation_issue
WHERE identity_key = 'finding_uid' AND status = 'resolved' AND resolved_at IS NOT NULL
ORDER BY file_name, byte_offset LIMIT ?''',
(max_rows + 1,),
).fetchall()
finally:
connection.close()
if len(rows) > max_rows:
raise RuntimeError('reviewed keycheck corruption count exceeds its bound')
reviewed = {}
for row in rows:
file_name = str(row['file_name'] or '')
offset = int(row['byte_offset'])
length = int(row['byte_length'])
digest = str(row['record_sha256'] or '').lower()
classification = str(row['classification'] or '')
if (
not file_name or os.path.basename(file_name) != file_name
or offset < 0 or length <= 0
or not re.fullmatch(r'[a-f0-9]{64}', digest)
or classification not in REVIEWED_CORRUPTION_CLASSIFICATIONS
):
raise RuntimeError('reviewed keycheck corruption metadata is invalid')
file_rows = reviewed.setdefault(file_name, {})
if offset in file_rows:
raise RuntimeError('reviewed keycheck corruption offsets are ambiguous')
file_rows[offset] = {
'device': str(row['file_device']),
'inode': str(row['file_inode']),
'size': int(row['file_size']),
'mtime_ns': str(row['file_mtime_ns']),
'length': length,
'sha256': digest,
'classification': classification,
}
return reviewed
def reviewed_corruptions_for_file(reviewed, path, signature):
rows = (reviewed or {}).get(os.path.basename(path), {})
if not rows:
return {}
actual = {
'device': str(signature.get('device', 0)),
'inode': str(signature.get('inode', 0)),
'size': int(signature.get('size', -1)),
'mtime_ns': str(signature.get('mtime_ns', -1)),
}
for issue in rows.values():
expected = {key: issue[key] for key in ('device', 'inode', 'size', 'mtime_ns')}
if actual != expected:
raise RuntimeError(f'reviewed keycheck corruption file identity changed: {path}')
return rows
def skip_reviewed_corruption(path, offset, length, digest, reviewed):
issue = (reviewed or {}).get(int(offset))
if issue is None:
return False
if int(issue['length']) != int(length) or issue['sha256'] != str(digest or '').lower():
raise RuntimeError(f'reviewed keycheck corruption record changed: {path}:byte:{offset}')
print(
f"Warning: skipped exact reviewed keycheck corruption at {path}:byte:{offset}; "
f"classification={issue['classification']}",
flush=True,
)
return True
def _drain_oversized_jsonl_identity(handle, first_chunk):
digest = hashlib.sha256(first_chunk)
length = len(first_chunk)
if first_chunk.endswith(b"\n"):
return True, length, digest.hexdigest()
while True:
chunk = handle.readline(64 * 1024)
if not chunk:
return False, length, digest.hexdigest()
digest.update(chunk)
length += len(chunk)
if chunk.endswith(b"\n"):
return True, length, digest.hexdigest()
def _drain_oversized_jsonl_record(handle, first_chunk):
return _drain_oversized_jsonl_identity(handle, first_chunk)[0]
def _warn_skipped_oversized(path, line_offset, max_line_bytes):
print(
f"Warning: skipped oversized committed keycheck input at {path}:byte:{line_offset}; "
f"line limit={max_line_bytes}",
flush=True,
)
def checkpoint_matches_signature(path, state, signature):
if not isinstance(state, dict) or state.get("path") != os.path.abspath(path):
return False
state_inode = int(state.get("inode", 0) or 0)
signature_inode = int(signature.get("inode", 0) or 0)
if not state_inode or not signature_inode or state_inode != signature_inode:
return False
state_device = int(state.get("device", 0) or 0)
signature_device = int(signature.get("device", 0) or 0)
if (state_device or signature_device) and (
not state_device or not signature_device or state_device != signature_device
):
return False
state_size = int(state.get("size", -1))
signature_size = int(signature.get("size", 0))
if state_size < 0 or state_size > signature_size:
return False
if state_size == signature_size:
state_mtime = int(state.get("mtime_ns", -1))
signature_mtime = int(signature.get("mtime_ns", -2))
if state_mtime < 0 or state_mtime != signature_mtime:
return False
return True
def input_start_offset(input_file, state, signature):
if truthy_env("KEYCHECK_DISABLE_HIGH_WATERMARK"):
tail_bytes = tail_fallback_bytes()
return max(0, signature["size"] - tail_bytes) if tail_bytes > 0 else 0, "tail_disabled" if tail_bytes > 0 else "full_disabled"
state = state or {}
if checkpoint_matches_signature(input_file, state, signature):
offset = int(state.get("offset", 0) or 0)
if 0 <= offset <= signature["size"]:
return offset, "high_watermark"
return 0, "full_replay" if state else "full_initial"
def iter_jsonl_input(input_file):
physical_segments = physical_jsonl_segments(input_file)
if not os.path.exists(input_file) and not physical_segments:
print(f"Warning: {input_file} not found. Nothing to read.")
return
manifest_path = jsonl_manifest_path(input_file)
if os.path.exists(manifest_path) or physical_segments:
yield from iter_segmented_jsonl_input(input_file, manifest_path)
return
service = keycheck_service_name()
state_file = keycheck_state_path(service)
state = read_json_file(state_file, {}) or {}
mode = "unopened"
final_offset = 0
processed = 0
skipped_oversized = 0
previous_skipped_oversized = 0
completed_read = False
checkpoint_signature = None
max_line_bytes = keycheck_input_max_line_bytes()
reviewed = load_reviewed_keycheck_corruptions(input_file)
skipped_reviewed = 0
try:
with open(input_file, "rb") as f:
signature = input_file_signature(input_file, f)
file_reviews = reviewed_corruptions_for_file(reviewed, input_file, signature)
start_offset, mode = input_start_offset(input_file, state, signature)
previous_skipped_oversized = (
int(state.get("skipped_oversized", 0) or 0)
if mode == "high_watermark" else 0
)
final_offset = start_offset
print(
f"Input reader: service={service or 'unknown'} mode={mode} "
f"offset={start_offset} size={signature['size']} state={state_file}",
flush=True,
)
if start_offset > 0:
f.seek(start_offset)
if mode.startswith("tail"):
boundary_offset = f.tell()
skipped = f.readline(max_line_bytes + 1)
if len(skipped) > max_line_bytes:
if not _drain_oversized_jsonl_record(f, skipped):
raise RuntimeError(f"torn oversized keycheck tail boundary record in {input_file}")
_warn_skipped_oversized(input_file, boundary_offset, max_line_bytes)
skipped_oversized += 1
elif skipped and not skipped.endswith(b"\n"):
raise RuntimeError(f"torn keycheck tail boundary record in {input_file}")
final_offset = f.tell()
while True:
line_offset = f.tell()
raw_line = f.readline(max_line_bytes + 1)
if not raw_line:
checkpoint_signature = input_file_signature(input_file, f)
completed_read = True
break
final_offset = f.tell()
if len(raw_line) > max_line_bytes:
complete, record_length, record_digest = _drain_oversized_jsonl_identity(f, raw_line)
if not complete:
final_offset = line_offset
completed_read = False
raise RuntimeError(f"torn oversized committed keycheck input at {input_file}:{line_offset}")
final_offset = f.tell()
if skip_reviewed_corruption(
input_file, line_offset, record_length, record_digest, file_reviews,
):
skipped_oversized += 1
skipped_reviewed += 1
continue
skipped_oversized += 1
_warn_skipped_oversized(input_file, line_offset, max_line_bytes)
continue
if not raw_line.endswith(b"\n"):
if skip_reviewed_corruption(
input_file, line_offset, len(raw_line), hashlib.sha256(raw_line).hexdigest(), file_reviews,
):
skipped_reviewed += 1
continue
final_offset = line_offset
completed_read = False
raise RuntimeError(f"torn committed keycheck input at {input_file}:{line_offset}")
try:
line = raw_line.decode("utf-8")
data = json.loads(line)
except (UnicodeDecodeError, ValueError) as exc:
if skip_reviewed_corruption(
input_file, line_offset, len(raw_line), hashlib.sha256(raw_line).hexdigest(), file_reviews,
):
skipped_reviewed += 1
continue
final_offset = line_offset
completed_read = False
kind = "torn" if not raw_line.endswith(b"\n") else "invalid"
raise RuntimeError(f"{kind} committed keycheck input at {input_file}:{line_offset}") from exc
processed += 1
yield {
"source": f"{input_file}:byte:{line_offset}",
"line_offset": line_offset,
"data": data,
}
finally:
if completed_read and checkpoint_signature is not None:
new_state = {
**checkpoint_signature,
"offset": int(final_offset),
"processed": int(processed),
"skipped_oversized": previous_skipped_oversized + int(skipped_oversized),
"skipped_reviewed_corrupt": int(state.get('skipped_reviewed_corrupt', 0) or 0) + skipped_reviewed,
"mode": mode,
"updated_at": now_iso(),
}
write_json_file(state_file, new_state)
print(
f"Input reader done: service={service or 'unknown'} processed={processed} "
f"skipped_oversized={skipped_oversized} new_offset={final_offset}",
flush=True,
)
else:
print(
f"Input reader stopped before EOF: service={service or 'unknown'} processed={processed}; "
f"state offset unchanged",
flush=True,
)
def manifest_input_files(input_file, manifest):
root = os.path.dirname(os.path.abspath(input_file))
physical = physical_jsonl_segments(input_file)
files = [path for _, path in physical]
listed_names = {
os.path.basename(str(segment.get("path") or segment.get("name") or ""))
for segment in (manifest.get("segments") or []) if isinstance(segment, dict)
}
missing = sorted(name for name in listed_names if name and name not in {os.path.basename(path) for path in files})
if missing:
raise RuntimeError(f"manifest-listed keycheck segment is missing: {missing[0]}")
current = os.path.abspath(input_file)
if current and os.path.exists(current):
files.append(os.path.abspath(current))
seen = set()
out = []
for path in files:
if path not in seen:
seen.add(path)
out.append(path)
return out
def iter_segmented_jsonl_input(input_file, manifest_path):
service = keycheck_service_name()
state_file = keycheck_state_path(service)
state = read_json_file(state_file, {}) or {}
manifest = read_json_file(manifest_path, {}) or {}
files_state = state.get("files") if isinstance(state.get("files"), dict) else {}
files = manifest_input_files(input_file, manifest)
first_manifest_run = not bool(files_state)
force_replay = truthy_env("KEYCHECK_DISABLE_HIGH_WATERMARK")
current_path = os.path.abspath(manifest.get("current_path") or input_file)
if current_path != os.path.abspath(input_file):
raise RuntimeError("keycheck manifest current path does not match the configured input")
# Physical segments are always discoverable and downstream publication is
# idempotent. Never seek past current-file bytes based on mutable manifest
# state; a writer could truncate and append after any generation check.
skip_current = 0
physical = physical_jsonl_segments(input_file)
total_processed = 0
total_skipped_oversized = 0
max_line_bytes = keycheck_input_max_line_bytes()
reviewed = load_reviewed_keycheck_corruptions(input_file)
total_skipped_reviewed = 0
print(
f"Input reader: service={service or 'unknown'} mode=manifest files={len(files)} "
f"state={state_file}",
flush=True,
)
try:
for path in files:
file_state = files_state.get(os.path.abspath(path)) or files_state.get(path) or {}
processed = 0
skipped_oversized = 0
skipped_reviewed = 0
with open(path, "rb") as f:
signature = input_file_signature(path, f)
file_reviews = reviewed_corruptions_for_file(reviewed, path, signature)
if force_replay:
offset = 0
previous_skipped_oversized = 0
previous_skipped_reviewed = 0
elif checkpoint_matches_signature(path, file_state, signature):
offset = int(file_state.get("offset", 0) or 0)
previous_skipped_oversized = int(file_state.get("skipped_oversized", 0) or 0)
previous_skipped_reviewed = int(file_state.get('skipped_reviewed_corrupt', 0) or 0)
else:
offset = 0
previous_skipped_oversized = 0
previous_skipped_reviewed = 0
if offset < 0 or offset > signature["size"]:
offset = 0
previous_skipped_oversized = 0
previous_skipped_reviewed = 0
if not force_replay and first_manifest_run and os.path.abspath(path) == current_path:
offset, _ = input_start_offset(path, {}, signature)
if os.path.abspath(path) == current_path:
offset = max(offset, min(skip_current, signature["size"]))
final_offset = offset
f.seek(offset)
while True:
line_offset = f.tell()
raw_line = f.readline(max_line_bytes + 1)
if not raw_line:
break
final_offset = f.tell()
if len(raw_line) > max_line_bytes:
complete, record_length, record_digest = _drain_oversized_jsonl_identity(f, raw_line)
if not complete:
final_offset = line_offset
raise RuntimeError(f"torn oversized committed keycheck input at {path}:{line_offset}")
final_offset = f.tell()
if skip_reviewed_corruption(
path, line_offset, record_length, record_digest, file_reviews,
):
skipped_oversized += 1
skipped_reviewed += 1
total_skipped_reviewed += 1
continue
skipped_oversized += 1
_warn_skipped_oversized(path, line_offset, max_line_bytes)
continue
if not raw_line.endswith(b"\n"):
if skip_reviewed_corruption(
path, line_offset, len(raw_line), hashlib.sha256(raw_line).hexdigest(), file_reviews,
):
skipped_reviewed += 1
total_skipped_reviewed += 1
continue
final_offset = line_offset
raise RuntimeError(f"torn committed keycheck input at {path}:{line_offset}")
try:
line = raw_line.decode("utf-8")
data = json.loads(line)
except (UnicodeDecodeError, ValueError) as exc:
if skip_reviewed_corruption(
path, line_offset, len(raw_line), hashlib.sha256(raw_line).hexdigest(), file_reviews,
):
skipped_reviewed += 1
total_skipped_reviewed += 1
continue
final_offset = line_offset
kind = "torn" if not raw_line.endswith(b"\n") else "invalid"
raise RuntimeError(f"{kind} committed keycheck input at {path}:{line_offset}") from exc
processed += 1
total_processed += 1
yield {
"source": f"{path}:byte:{line_offset}",
"line_offset": line_offset,
"data": data,
}
checkpoint_signature = input_file_signature(path, f)
files_state[os.path.abspath(path)] = {
**checkpoint_signature,
"offset": int(final_offset),
"done": int(final_offset) >= int(checkpoint_signature["size"]),
"processed": int(processed),
"skipped_oversized": previous_skipped_oversized + int(skipped_oversized),
"skipped_reviewed_corrupt": previous_skipped_reviewed + skipped_reviewed,
"updated_at": now_iso(),
}
total_skipped_oversized += skipped_oversized
finally:
new_state = {
"manifest_path": os.path.abspath(manifest_path),
"current_file": os.path.abspath(input_file),
"files": files_state,
"processed": int(total_processed),
"skipped_oversized": sum(
int(item.get("skipped_oversized", 0) or 0)
for item in files_state.values() if isinstance(item, dict)
),
"skipped_reviewed_corrupt": sum(
int(item.get('skipped_reviewed_corrupt', 0) or 0)
for item in files_state.values() if isinstance(item, dict)
),
"mode": "manifest",
"updated_at": now_iso(),
}
write_json_file(state_file, new_state)
print(
f"Input reader done: service={service or 'unknown'} mode=manifest "
f"processed={total_processed} skipped_oversized={total_skipped_oversized}",
flush=True,
)
def mask_secret(value):
if not value or len(value) < 12:
return value or ""
return f"{value[:8]}...{value[-4:]}"
def keycheck_status_group(status):
status = str(status or "UNKNOWN").strip().upper()
alive = {"ALIVE", "VALID", "VALID_2FA", "VALID_RATE_LIMITED", "BEDROCK", "ADMIN", "VERTEX", "CANARY", "FOUNDRY"}
dead = {"DEAD", "INVALID", "EXPIRED", "LEAKED_REVOKED", "INVALID_OR_REVOKED"}
restricted = {"RESTRICTED", "API_DISABLED", "ACCESS_DENIED", "QUARANTINED", "DISABLED"}
no_balance = {"NO_BALANCE", "NO_QUOTA", "LIMITED_OR_NO_BALANCE", "LIMITED_OR_QUOTA"}
no_context = {"NO_CONTEXT", "NO_USERNAME", "NO_TARGET", "NO_TARGET_MODELS", "NO_GENERATION_MODEL"}
limited = {"LIMITED", "RATE_LIMITED"}
network = {"NETWORK", "NETWORK_ERROR"}
if status in alive:
return "alive"
if status in dead:
return "dead"
if status in restricted:
return "restricted"
if status in no_balance:
return "no_balance"
if status in no_context:
return "no_context"
if status in limited:
return "limited"
if status in network:
return "network"
return "unknown"
def redact_value(value, raw_key):
if isinstance(value, dict):
return {k: redact_value(v, raw_key) for k, v in value.items() if k not in ("finding", "raw_secret", "raw", "raw_v2")}
if isinstance(value, list):
return [redact_value(item, raw_key) for item in value]
if isinstance(value, str):
text = value.replace(raw_key, "***REDACTED***") if raw_key else value
return text[:4000]
return value
def result_message(result):
if not isinstance(result, dict):
return ""
if result.get("message"):
return result.get("message")
error = result.get("error")
if isinstance(error, dict):
return error.get("message") or json.dumps(error, ensure_ascii=False)[:1000]
return error or ""
def finding_secret_hash(finding):
if not isinstance(finding, dict):
return ""
raw = extract_raw_secret(finding)
return sha256_text(raw) if raw else ""
def clear_provider_routing_evidence_cache():
global PROVIDER_ROUTING_EVIDENCE_CACHE_AUTHORITY, PROVIDER_ROUTING_EVIDENCE_DB_FAILED
PROVIDER_ROUTING_EVIDENCE_CACHE.clear()
PROVIDER_ROUTING_EVIDENCE_CACHE_AUTHORITY = None
PROVIDER_ROUTING_EVIDENCE_DB_FAILED = False
def _provider_routing_explicit_evidence(finding):
if not isinstance(finding, dict):
return set()
detector = str(finding.get("DetectorName") or "").strip().lower()
detector_name = detector
if detector == "customregex":
extra = finding.get("ExtraData") if isinstance(finding.get("ExtraData"), dict) else {}
detector_name = str(extra.get("name") or "").strip().lower()
evidence = set()
if detector_name in QWEN_EXPLICIT_ROUTING_DETECTORS:
evidence.add("qwen")
if detector_name in DEEPSEEK_EXPLICIT_ROUTING_DETECTORS:
evidence.add("deepseek")
if detector_name in KIMI_EXPLICIT_ROUTING_DETECTORS:
evidence.add("kimi")
if detector_name in ZAI_EXPLICIT_ROUTING_DETECTORS:
evidence.add("zai")
context = finding.get("ScannerContext") if isinstance(finding.get("ScannerContext"), dict) else {}
if context.get("provider_hint_source") != EXPLICIT_ASSIGNMENT_PROVIDER_HINT_SOURCE:
return evidence
hint = context.get("provider_hint")
if hint == AMBIGUOUS_QWEN_DEEPSEEK_HINT:
evidence.update(QWEN_DEEPSEEK_PROVIDERS)
return evidence
if hint == AMBIGUOUS_GENERIC_SK_HINT:
evidence.update(GENERIC_SK_PROVIDERS)
return evidence
if hint in GENERIC_SK_PROVIDERS:
evidence.add(hint)
return evidence
return set(GENERIC_SK_PROVIDERS)
def _provider_routing_cache_limits():
return {
"items": min(100000, max(1, env_int("KEYCHECK_PROVIDER_EVIDENCE_CACHE_ITEMS", 4096))),
"rows": min(1024, max(1, env_int("KEYCHECK_PROVIDER_EVIDENCE_MAX_ROWS", 64))),
"json_chars": min(
1024 * 1024,
max(1024, env_int("KEYCHECK_PROVIDER_EVIDENCE_MAX_JSON_CHARS", 256 * 1024)),
),
}
def _provider_routing_database_authority():
db_url = str(os.getenv("KEYCHECK_DB_URL") or "").strip()
db_path = "" if db_url else str(os.getenv("KEYCHECK_DB_PATH") or "").strip()
identity = db_url or (os.path.abspath(db_path) if db_path else "")
return db_url, db_path, sha256_text(identity) if identity else ""
def _load_provider_routing_evidence(secret_hash, limits):
db_url, db_path, _ = _provider_routing_database_authority()
if not db_url and not db_path:
raise RuntimeError("canonical keycheck database is unavailable")
db = ScannerDB(db_path=db_path or None, db_url=db_url or "", initialize=False)
try:
if not db.enabled:
raise RuntimeError("canonical keycheck database connection is unavailable")
db.require_runtime_safety_schema()
if getattr(db.conn, "is_postgres", False):
statement_timeout_ms = min(
30000,
max(250, env_int("KEYCHECK_PROVIDER_EVIDENCE_STATEMENT_TIMEOUT_MS", 5000)),
)
lock_timeout_ms = min(
statement_timeout_ms,
max(100, env_int("KEYCHECK_PROVIDER_EVIDENCE_LOCK_TIMEOUT_MS", 1000)),
)
db.conn.execute("SELECT set_config('statement_timeout', ?, false)", (f"{statement_timeout_ms}ms",))
db.conn.execute("SELECT set_config('lock_timeout', ?, false)", (f"{lock_timeout_ms}ms",))
rows = db.provider_routing_finding_rows(
secret_hash,
limits["rows"] + 1,
limits["json_chars"],
)
finally:
db.close()
if len(rows) > limits["rows"]:
return frozenset(GENERIC_SK_PROVIDERS)
evidence = set()
for row in rows:
if bool(row["truncated"]):
return frozenset(GENERIC_SK_PROVIDERS)
raw_finding_json = row["raw_finding_json"] or ""
if not raw_finding_json:
detector_name = str(row["detector_name"] or "").strip().lower()
detector_evidence = _provider_routing_explicit_evidence({
"DetectorName": detector_name,
})
if detector_evidence:
evidence.update(detector_evidence)
elif detector_name not in GENERIC_SK_NON_EXPLICIT_ROUTING_DETECTORS:
return frozenset(GENERIC_SK_PROVIDERS)
if GENERIC_SK_PROVIDERS.issubset(evidence):
return frozenset(GENERIC_SK_PROVIDERS)
continue
try:
finding = json.loads(raw_finding_json)
except (TypeError, ValueError, json.JSONDecodeError):
return frozenset(GENERIC_SK_PROVIDERS)
if isinstance(finding, dict) and isinstance(finding.get("finding"), dict):
finding = finding["finding"]
if not isinstance(finding, dict):
return frozenset(GENERIC_SK_PROVIDERS)
raw = extract_raw_secret(finding)
if not raw or sha256_text(raw) != secret_hash:
return frozenset(GENERIC_SK_PROVIDERS)
evidence.update(_provider_routing_explicit_evidence(finding))
if GENERIC_SK_PROVIDERS.issubset(evidence):
return frozenset(GENERIC_SK_PROVIDERS)
return frozenset(evidence)
def global_provider_routing_evidence(raw_key):
"""Return persisted generic sk-key provider evidence without retaining the raw key."""
global PROVIDER_ROUTING_EVIDENCE_CACHE_AUTHORITY, PROVIDER_ROUTING_EVIDENCE_DB_FAILED
PROVIDER_ROUTING_EVIDENCE_DB_FAILED = False
secret_hash = sha256_text(raw_key)
if not secret_hash:
return set(GENERIC_SK_PROVIDERS)
_, _, authority = _provider_routing_database_authority()
if authority != PROVIDER_ROUTING_EVIDENCE_CACHE_AUTHORITY:
PROVIDER_ROUTING_EVIDENCE_CACHE.clear()
PROVIDER_ROUTING_EVIDENCE_CACHE_AUTHORITY = authority
PROVIDER_ROUTING_EVIDENCE_DB_FAILED = False
cached = PROVIDER_ROUTING_EVIDENCE_CACHE.get(secret_hash)
if cached is not None:
evidence, retry_at = cached
if retry_at is None or retry_at > time.monotonic():
PROVIDER_ROUTING_EVIDENCE_CACHE.move_to_end(secret_hash)
PROVIDER_ROUTING_EVIDENCE_DB_FAILED = retry_at is not None
return set(evidence)
del PROVIDER_ROUTING_EVIDENCE_CACHE[secret_hash]
limits = _provider_routing_cache_limits()
try:
evidence = _load_provider_routing_evidence(secret_hash, limits)
except Exception:
PROVIDER_ROUTING_EVIDENCE_DB_FAILED = True
evidence = frozenset(GENERIC_SK_PROVIDERS)
retry_at = time.monotonic() + min(
5,
max(1, env_int("KEYCHECK_PROVIDER_EVIDENCE_ERROR_CACHE_SEC", 1)),
)
else:
retry_at = None
if evidence == GENERIC_SK_PROVIDERS:
PROVIDER_ROUTING_EVIDENCE_CACHE[secret_hash] = (evidence, retry_at)
PROVIDER_ROUTING_EVIDENCE_CACHE.move_to_end(secret_hash)
while len(PROVIDER_ROUTING_EVIDENCE_CACHE) > limits["items"]:
PROVIDER_ROUTING_EVIDENCE_CACHE.popitem(last=False)
return set(evidence)
def provider_routing_database_failed():
return bool(PROVIDER_ROUTING_EVIDENCE_DB_FAILED)
def combined_provider_routing_hint(raw_key, local_hint=""):
global PROVIDER_ROUTING_EVIDENCE_DB_FAILED
PROVIDER_ROUTING_EVIDENCE_DB_FAILED = False
evidence = set()
if local_hint == AMBIGUOUS_QWEN_DEEPSEEK_HINT:
evidence.update(QWEN_DEEPSEEK_PROVIDERS)
elif local_hint == AMBIGUOUS_GENERIC_SK_HINT:
evidence.update(GENERIC_SK_PROVIDERS)
elif local_hint in GENERIC_SK_PROVIDERS:
evidence.add(local_hint)
elif keycheck_input_mode() == 'postgres':
service = keycheck_service_name()
if service in GENERIC_SK_PROVIDERS:
evidence.add(service)
if len(evidence) > 1:
return (
AMBIGUOUS_QWEN_DEEPSEEK_HINT
if evidence == set(QWEN_DEEPSEEK_PROVIDERS)
else AMBIGUOUS_GENERIC_SK_HINT
)
evidence.update(global_provider_routing_evidence(raw_key))
if len(evidence) > 1:
return (
AMBIGUOUS_QWEN_DEEPSEEK_HINT
if evidence == set(QWEN_DEEPSEEK_PROVIDERS)
else AMBIGUOUS_GENERIC_SK_HINT
)
if not evidence:
return ""
provider = next(iter(evidence))
if provider not in GENERIC_SK_PROVIDERS:
return AMBIGUOUS_GENERIC_SK_HINT
return provider
def strip_finding_nearby_context(finding):
if not isinstance(finding, dict):
return finding
output = dict(finding)
context = output.get("ScannerContext")
if isinstance(context, dict) and "nearby" in context:
output["ScannerContext"] = {key: value for key, value in context.items() if key != "nearby"}
return output
def finding_detector_secret_hash(finding):
if not isinstance(finding, dict):
return ""
raw = extract_raw_secret(finding)
secret_hash = sha256_text(raw) if raw else ""
fallback_hash = sha256_text(json_dumps(finding)) if not secret_hash else ""
detector = str(finding.get("DetectorName") or finding.get("DetectorType") or "")
return sha256_text("|".join([detector, secret_hash or fallback_hash])) if detector or secret_hash or fallback_hash else ""
def finding_uid(finding):
if not isinstance(finding, dict):
return ""
return str(finding.get("finding_uid") or finding.get("FindingUID") or "")
def keycheck_event_id(service, key_hash, status, source, finding_uid_value, checked_at, result_source, payload):
explicit = payload.get("event_id") if isinstance(payload, dict) else ""
if explicit:
return str(explicit)
cached_id = payload.get("cached_occurrence_id") if isinstance(payload, dict) else ""
if cached_id:
return sha256_text("|".join([str(service or ""), "cached", str(cached_id)]))
return sha256_text("|".join([
str(service or ""),
str(key_hash or ""),
str(status or ""),
str(source or ""),
str(finding_uid_value or ""),
str(checked_at or ""),
str(result_source or "api_check"),
]))
def write_keycheck_event(service, results_file, key, result, source="", finding=None, detector=None, result_source="api_check"):
result = result if isinstance(result, dict) else {"status": str(result or "UNKNOWN")}
finding = strip_finding_nearby_context(finding)
status = str(result.get("status") or "UNKNOWN").upper()
checked_at = result.get("checked_at") or now_iso()
detector_name = detector or get_detector_name(finding or {}) or result.get("detector") or ""
key_hash = result.get("key_hash") or sha256_text(key)
secret_hash = result.get("secret_hash") or finding_secret_hash(finding) or key_hash
detector_secret_hash = result.get("detector_secret_hash") or finding_detector_secret_hash(finding)
finding_uid_value = result.get("finding_uid") or finding_uid(finding)
payload = {
"key_masked": result.get("key_masked") or mask_secret(key),
"key_hash": key_hash,
"secret_hash": secret_hash,
"detector_secret_hash": detector_secret_hash,
"finding_uid": finding_uid_value,
"detector": detector_name,
"source": source,
"finding": finding or {},
"checked_at": checked_at,
"result_source": result.get("result_source") or result_source or "api_check",
**result,
}
payload["status"] = status
payload["checked_at"] = checked_at
payload["result_source"] = result.get("result_source") or result_source or "api_check"
payload["event_id"] = keycheck_event_id(
service,
payload.get("key_hash"),
payload.get("status"),
source,
payload.get("finding_uid"),
payload.get("checked_at"),
payload.get("result_source"),
payload,
)
if keycheck_input_mode() == 'postgres':
global _ACTIVE_DB_CANDIDATE
candidate = _ACTIVE_DB_CANDIDATE
if not candidate or str(candidate.get('service') or '') != str(service or ''):
raise RuntimeError('keycheck result has no exact active PostgreSQL candidate lease')
payload['event_id'] = sha256_text('|'.join((
'truf-keycheck-event-v1', str(candidate['id']), str(candidate['attempts']),
)))
metadata = {
name: value for name, value in payload.items()
if name not in ('finding', 'key_masked', 'key_hash', 'secret_hash')
}
resolved_service = str(payload.get('resolved_provider') or '').strip().lower()
if resolved_service:
metadata['resolution_origin_service'] = str(service or '').strip().lower()
outcome = _postgres_candidate_db().complete_keycheck_candidate(
candidate['id'], candidate['lease_token'], payload['event_id'],
payload['status'], keycheck_status_group(payload['status']),
checked_at=payload['checked_at'], message=result_message(payload),
metadata=redact_value(metadata, key), result_source=payload['result_source'],
resolved_service=resolved_service,
)
if not outcome or not outcome.get('completed'):
raise RuntimeError('PostgreSQL keycheck candidate completion lost its fence')
candidate['_completed'] = True
else:
append_jsonl(results_file, payload)
return payload
def record_validation_result(service, key, result, source="", finding=None, detector=None, db_path=None):
if keycheck_input_mode() == 'postgres':
candidate = _ACTIVE_DB_CANDIDATE
return bool(candidate and candidate.get('_completed'))
db_path = db_path or os.getenv("KEYCHECK_DB_PATH") or os.getenv("SCANNER_DB_PATH") or os.getenv("SCAN_DB_PATH")
db_url = os.getenv("KEYCHECK_DB_URL") or os.getenv("SCANNER_DB_URL") or os.getenv("DATABASE_URL")
if not db_path and not db_url:
return
if not truthy_env("KEYCHECK_DB_INLINE", False):
return False
result = result if isinstance(result, dict) else {"status": str(result or "UNKNOWN")}
finding = strip_finding_nearby_context(finding)
status = str(result.get("status") or "UNKNOWN").upper()
secret_hash = finding_secret_hash(finding) or sha256_text(key)
detector_secret_hash = finding_detector_secret_hash(finding)
metadata = redact_value({**result, "source_line": source, "detector_secret_hash": detector_secret_hash}, key)
result_source = metadata.get("result_source") or "api_check"
try:
db = ScannerDB(db_path=db_path, db_url=db_url, initialize=False)
try:
db.require_runtime_safety_schema()
if getattr(db.conn, "is_postgres", False):
statement_timeout_ms = max(1000, env_int("KEYCHECK_DB_STATEMENT_TIMEOUT_MS", 8000))
lock_timeout_ms = max(250, env_int("KEYCHECK_DB_LOCK_TIMEOUT_MS", 2000))
db.conn.execute("SELECT set_config('statement_timeout', ?, false)", (f"{statement_timeout_ms}ms",))
db.conn.execute("SELECT set_config('lock_timeout', ?, false)", (f"{lock_timeout_ms}ms",))
ok = db.record_keycheck_result(
service=service,
status=status,
status_group=keycheck_status_group(status),
checked_at=result.get("checked_at") or now_iso(),
key_hash=sha256_text(key),
secret_hash=secret_hash,
key_masked=result.get("key_masked") or mask_secret(key),
detector_name=detector or get_detector_name(finding or {}) or result.get("detector") or "",
message=result_message(result),
metadata=metadata,
link_findings=truthy_env("KEYCHECK_DB_LINK_FINDINGS", False),
source_line=source,
detector_secret_hash=detector_secret_hash,
)
if not ok:
raise RuntimeError("keycheck DB write returned false")
finally:
db.close()
record_db_write_metric(service, True, result_source, status)
return True
except Exception as exc:
print(f"Warning: unable to record keycheck result in DB: {exc}")
record_db_write_metric(service, False, result_source, status, str(exc))
return False
def record_db_write_metric(service, ok, result_source, status, error=""):
output_dir = os.getenv("KEYCHECK_OUTPUT_DIR") or service_output_dir(service or keycheck_service_name() or "default")
require_private_directory(output_dir, create=True)
payload = {
"service": service or keycheck_service_name(),
"ok": bool(ok),
"result_source": result_source or "api_check",
"status": status,
"error": str(error or "")[:500],
"created_at": now_iso(),
}
try:
append_jsonl(os.path.join(output_dir, "db_write_metrics.jsonl"), payload)
if not ok:
with private_append_writer(os.path.join(output_dir, "db_write_failures.log")) as f:
f.write(f"{payload['created_at']}\t{payload['status']}\t{payload['result_source']}\t{payload['error']}\n")
except OSError:
pass
def normalize_status_key(line):
line = line.strip()
if not line:
return None
if "\t" in line:
return line.split("\t", 1)[0].strip()
return line.strip()
def iter_bounded_text_lines(path):
if keycheck_input_mode() == 'postgres':
return
max_bytes = max(1, env_int('KEYCHECK_INPUT_LIST_MAX_BYTES', 32 * 1024 * 1024))
max_items = max(1, env_int('KEYCHECK_INPUT_LIST_MAX_ITEMS', 100000))
max_line_bytes = status_projection_line_max_bytes()
if not os.path.exists(path):
return
if os.path.getsize(path) > max_bytes:
raise RuntimeError(f'keycheck input list exceeds its aggregate byte bound: {path}')
with open(path, 'rb') as handle:
for index, raw_line in enumerate(handle, 1):
if index > max_items:
raise RuntimeError(f'keycheck input list exceeds its item bound: {path}')
if len(raw_line) > max_line_bytes:
raise RuntimeError(f'keycheck input list line exceeds its byte bound: {path}:{index}')
yield raw_line.decode('utf-8', errors='replace')
def load_keys_from_file(path):
if keycheck_input_mode() == 'postgres':
return set()
if not os.path.exists(path):
return set()
return {key for key in (normalize_status_key(line) for line in iter_bounded_text_lines(path)) if key}
def load_checked_statuses(path):
statuses = {}
if keycheck_input_mode() == 'postgres':
return statuses
if not os.path.exists(path):
return statuses
for line in iter_bounded_text_lines(path):
parts = line.rstrip("\n").split("\t")
if not parts or not parts[0]:
continue
statuses[parts[0]] = parts[1] if len(parts) > 1 else "UNKNOWN"
return statuses
def ensure_output_files(paths):
if keycheck_input_mode() == 'postgres':
return
for path in paths:
parent = os.path.dirname(path)
if parent:
require_private_directory(parent, create=True)
if os.path.lexists(path):
require_private_file(path)
continue
with private_atomic_writer(path, binary=True, suffix=".empty.tmp"):
pass
def remove_key_from_files(key, paths):
if keycheck_input_mode() == 'postgres':
return
for path in paths:
if not os.path.exists(path):
continue
lock_path = f"{path}.lock"
lock = acquire_file_lock(lock_path, timeout_sec=30)
try:
require_private_file(path)
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()
with private_atomic_writer(path) as f:
for line in lines:
if normalize_status_key(line) != key:
f.write(line)
finally:
release_file_lock(lock, lock_path)
def append_status(path, key, status, message="", extra=""):
if keycheck_input_mode() == 'postgres':
return 'postgres-authoritative'
message = str(message or "").replace("\n", " ")[:1000]
extra = str(extra or "").replace("\n", " ")[:1000]
require_private_directory(os.path.dirname(os.path.abspath(path)), create=True)
if os.path.lexists(path):
reject_reparse_components(path)
lock_path = f"{path}.lock"
lock = acquire_file_lock(lock_path, timeout_sec=30)
try:
with private_append_writer(path) as f:
f.write(f"{key}\t{status}\t{message}\t{extra}\n")
finally:
release_file_lock(lock, lock_path)
def append_checked(path, key, status):
if keycheck_input_mode() == 'postgres':
return 'postgres-authoritative'
require_private_directory(os.path.dirname(os.path.abspath(path)), create=True)
if os.path.lexists(path):
reject_reparse_components(path)
lock_path = f"{path}.lock"
lock = acquire_file_lock(lock_path, timeout_sec=30)
try:
with private_append_writer(path) as f:
f.write(f"{key}\t{status}\t{now_iso()}\n")
finally:
release_file_lock(lock, lock_path)
def append_jsonl(path, payload):
if keycheck_input_mode() == 'postgres':
return 'postgres-authoritative'
parent = os.path.dirname(path)
if parent:
require_private_directory(parent, create=True)
if os.path.lexists(path):
reject_reparse_components(path)
fd = None
lock_path = f"{path}.lock"
try:
fd = acquire_file_lock(lock_path, env_int("KEYCHECK_JSONL_LOCK_STALE_SEC", 300))
repair_keycheck_jsonl_tail(path)
reconcile_keycheck_jsonl_segments(path)
if should_rotate_jsonl(path):
max_mb = env_int("KEYCHECK_RESULTS_MAX_MB", 32)
rotate_jsonl_if_needed(path, max_mb * 1024 * 1024)
with private_append_writer(path) as f:
f.write(json.dumps(payload, ensure_ascii=False, default=str) + "\n")
finally:
if fd is not None:
release_file_lock(fd, lock_path)
def load_known_keys(checked_file, status_files):
return set(load_known_statuses(checked_file, status_files).keys())
def normalize_status_files(status_files):
output = {}
if not status_files:
return output
if isinstance(status_files, dict):
for key, value in status_files.items():
key_text = str(key)
value_text = str(value)
if key_text.lower().endswith(".txt") or os.sep in key_text or "/" in key_text:
output[value_text.upper()] = key_text
else:
output[key_text.upper()] = value_text
else:
for path in status_files:
output["UNKNOWN"] = str(path)
return output
def status_transaction_journal_path(checked_file):
return os.path.join(
os.path.dirname(os.path.abspath(checked_file)),
STATUS_TRANSACTION_JOURNAL_FILENAME,
)
def status_transaction_lock_path(checked_file):
return os.path.join(
os.path.dirname(os.path.abspath(checked_file)),
STATUS_TRANSACTION_LOCK_FILENAME,
)
def _status_transaction_layout(checked_file, status_files):
checked_file = os.path.abspath(checked_file)
output_dir = os.path.dirname(checked_file)
require_private_directory(output_dir, create=True)
normalized = {
str(status or "").strip().upper(): os.path.abspath(path)
for status, path in normalize_status_files(status_files).items()
}
if not normalized or any(
not re.fullmatch(r"[A-Z][A-Z0-9_]{0,127}", status)
for status in normalized
):
raise ValueError("status transaction requires named status files")
status_paths = list(dict.fromkeys(
os.path.abspath(path) for path in normalized.values()
))
paths = [checked_file, *status_paths]
if any(os.path.normcase(os.path.dirname(path)) != os.path.normcase(output_dir) for path in paths):
raise ValueError("status transaction files must share one service directory")
if os.path.normcase(checked_file) in {os.path.normcase(path) for path in status_paths}:
raise ValueError("status transaction checked and status paths must be distinct")
ensure_output_files(paths)
return checked_file, normalized
def _status_transaction_key(key):
key = str(key or "")
if not key or any(character in key for character in ("\x00", "\t", "\r", "\n")):
raise ValueError("status transaction key is empty or contains a control separator")
if len(key.encode("utf-8", errors="strict")) > status_projection_line_max_bytes():
raise ValueError("status transaction key exceeds its byte bound")
return key
def _status_transaction_field(value):
return str(value or "").replace("\r", " ").replace("\n", " ")[:1000]
def _status_transaction_line(value, name):
if not isinstance(value, str):
raise ValueError(f"status transaction {name} must be text")
if not value.endswith("\n") or "\n" in value[:-1] or "\r" in value or "\x00" in value:
raise ValueError(f"status transaction {name} must be exactly one LF-terminated line")
if len(value.encode("utf-8", errors="strict")) > status_projection_line_max_bytes():
raise ValueError(f"status transaction {name} exceeds its byte bound")
return value
def _status_projection_line_matches_key(line, key):
value = str(line or "").rstrip("\r\n")
if value == key:
return True
if not value.startswith(key):
return False
suffix = value[len(key):]
return suffix.startswith("\t") or suffix.startswith(":")
def _validate_status_projection_line(line, key):
line = _status_transaction_line(line, "status line")
if not _status_projection_line_matches_key(line, key):
raise ValueError("status transaction status line does not contain its exact key")
return line
def _validate_checked_projection_line(line, key, status):
line = _status_transaction_line(line, "checked line")
parts = line[:-1].split("\t")
if len(parts) < 2 or parts[0] != key or parts[1] != status:
raise ValueError("status transaction checked line does not match its key and status")
return line
def _new_status_transaction(
key,
status,
status_path,
message,
extra,
status_line=None,
checked_line=None,
):
key = _status_transaction_key(key)
status = str(status or "UNKNOWN").strip().upper()
message = _status_transaction_field(message)
extra = _status_transaction_field(extra)
checked_at = now_iso()
status_line = _validate_status_projection_line(
status_line if status_line is not None else f"{key}\t{status}\t{message}\t{extra}\n",
key,
)
checked_line = _validate_checked_projection_line(
checked_line if checked_line is not None else f"{key}\t{status}\t{checked_at}\n",
key,
status,
)
transaction = {
"version": 2,
"transaction_id": uuid.uuid4().hex,
"key": key,
"status": status,
"status_file": os.path.basename(status_path),
"message": message,
"extra": extra,
"checked_at": checked_at,
"status_line": status_line,
"checked_line": checked_line,
}
encoded = json.dumps(transaction, ensure_ascii=True, sort_keys=True, separators=(",", ":")).encode("utf-8")
if len(encoded) + 1 > status_transaction_journal_max_bytes():
raise ValueError("status transaction journal exceeds its byte bound")
return transaction
def _validate_status_transaction(transaction, status_files):
if not isinstance(transaction, dict) or type(transaction.get("version")) is not int or transaction["version"] not in (1, 2):
raise RuntimeError("invalid status transaction journal version")
version = transaction["version"]
transaction_id = transaction.get("transaction_id")
if not isinstance(transaction_id, str) or not re.fullmatch(r"[a-f0-9]{32}", transaction_id):
raise RuntimeError("invalid status transaction identity")
raw_key = transaction.get("key")
if not isinstance(raw_key, str):
raise RuntimeError("invalid status transaction key")
try:
key = _status_transaction_key(raw_key)
except (UnicodeError, ValueError) as exc:
raise RuntimeError(f"invalid status transaction key: {exc}") from exc
raw_status = transaction.get("status")
if not isinstance(raw_status, str) or raw_status != raw_status.strip().upper():
raise RuntimeError("status transaction status is not canonical")
status = raw_status
if status not in status_files:
raise RuntimeError("status transaction targets an unknown projection")
status_file = transaction.get("status_file")
if not isinstance(status_file, str) or os.path.normcase(status_file) != os.path.normcase(
os.path.basename(status_files[status])
):
raise RuntimeError("status transaction target does not match its projection")
message = _status_transaction_field(transaction.get("message"))
extra = _status_transaction_field(transaction.get("extra"))
if message != transaction.get("message") or extra != transaction.get("extra"):
raise RuntimeError("status transaction fields are not canonical")
checked_at = transaction.get("checked_at")
if not isinstance(checked_at, str):
raise RuntimeError("invalid status transaction timestamp")
if not checked_at or len(checked_at) > 64 or any(character in checked_at for character in ("\t", "\r", "\n")):
raise RuntimeError("invalid status transaction timestamp")
expected_status_line = f"{key}\t{status}\t{message}\t{extra}\n"
expected_checked_line = f"{key}\t{status}\t{checked_at}\n"
try:
if version == 1:
if transaction.get("status_line") != expected_status_line or transaction.get("checked_line") != expected_checked_line:
raise RuntimeError("status transaction projection data is inconsistent")
_validate_status_projection_line(transaction["status_line"], key)
_validate_checked_projection_line(transaction["checked_line"], key, status)
else:
_validate_status_projection_line(transaction.get("status_line"), key)
_validate_checked_projection_line(transaction.get("checked_line"), key, status)
except (TypeError, UnicodeError, ValueError) as exc:
raise RuntimeError(f"invalid status transaction projection data: {exc}") from exc
encoded = json.dumps(transaction, ensure_ascii=True, sort_keys=True, separators=(",", ":")).encode("utf-8")
if len(encoded) + 1 > status_transaction_journal_max_bytes():
raise RuntimeError("status transaction journal exceeds its byte bound")
return transaction
def _rewrite_status_projection(path, key, replacement_line=None):
require_private_file(path)
with private_atomic_writer(path, suffix=".status.tmp") as output:
for line in iter_bounded_text_lines(path):
if _status_projection_line_matches_key(line, key):
continue
output.write(line if line.endswith("\n") else line + "\n")
if replacement_line is not None:
output.write(replacement_line)
def publish_status_transaction_target(transaction, status_files):
status_files = normalize_status_files(status_files)
_rewrite_status_projection(
status_files[transaction["status"]],
transaction["key"],
transaction["status_line"],
)
def publish_status_transaction_checked(transaction, checked_file):
_rewrite_status_projection(
checked_file,
transaction["key"],
transaction["checked_line"],
)
def remove_status_transaction_old_copies(transaction, status_files):
status_files = normalize_status_files(status_files)
target_path = os.path.normcase(os.path.abspath(status_files[transaction["status"]]))
visited = {target_path}
for _, path in sorted(status_files.items()):
normalized_path = os.path.normcase(os.path.abspath(path))
if normalized_path in visited:
continue
visited.add(normalized_path)
_rewrite_status_projection(path, transaction["key"])
def delete_status_transaction_journal(journal_path):
durable_unlink(journal_path)
def _apply_status_transaction(transaction, checked_file, status_files, journal_path):
transaction = _validate_status_transaction(transaction, status_files)
publish_status_transaction_target(transaction, status_files)
publish_status_transaction_checked(transaction, checked_file)
remove_status_transaction_old_copies(transaction, status_files)
delete_status_transaction_journal(journal_path)
def _recover_status_transaction_locked(checked_file, status_files, journal_path):
if not os.path.lexists(journal_path):
return False
transaction = read_private_json(journal_path, max_bytes=status_transaction_journal_max_bytes())
_apply_status_transaction(transaction, checked_file, status_files, journal_path)
return True
def recover_status_transaction(checked_file, status_files):
if keycheck_input_mode() == 'postgres':
return False
journal_path = status_transaction_journal_path(checked_file)
if not os.path.lexists(journal_path):
return False
checked_file, status_files = _status_transaction_layout(checked_file, status_files)
journal_path = status_transaction_journal_path(checked_file)
lock_path = status_transaction_lock_path(checked_file)
timeout = min(120, max(1, env_int("KEYCHECK_STATUS_TRANSACTION_LOCK_TIMEOUT_SEC", 30)))
lock = acquire_file_lock(lock_path, timeout_sec=timeout)
try:
return _recover_status_transaction_locked(checked_file, status_files, journal_path)
finally:
release_file_lock(lock, lock_path)
def commit_status_transaction(
checked_file,
status_files,
key,
status,
message="",
extra="",
status_line=None,
checked_line=None,
):
if keycheck_input_mode() == 'postgres':
return 'postgres-authoritative'
checked_file, status_files = _status_transaction_layout(checked_file, status_files)
status = str(status or "UNKNOWN").strip().upper()
if status not in status_files:
status = "UNKNOWN"
if status not in status_files:
raise ValueError("status transaction has no UNKNOWN projection")
journal_path = status_transaction_journal_path(checked_file)
lock_path = status_transaction_lock_path(checked_file)
timeout = min(120, max(1, env_int("KEYCHECK_STATUS_TRANSACTION_LOCK_TIMEOUT_SEC", 30)))
lock = acquire_file_lock(lock_path, timeout_sec=timeout)
try:
_recover_status_transaction_locked(checked_file, status_files, journal_path)
transaction = _new_status_transaction(
key,
status,
status_files[status],
message,
extra,
status_line,
checked_line,
)
encoded = json.dumps(
transaction, ensure_ascii=True, sort_keys=True, separators=(",", ":"),
).encode("utf-8") + b"\n"
if len(encoded) > status_transaction_journal_max_bytes():
raise ValueError("status transaction journal exceeds its byte bound")
with private_atomic_writer(journal_path, binary=True, suffix=".journal.tmp") as handle:
handle.write(encoded)
_apply_status_transaction(transaction, checked_file, status_files, journal_path)
return transaction["transaction_id"]
finally:
release_file_lock(lock, lock_path)
def load_known_statuses(checked_file, status_files):
if keycheck_input_mode() == 'postgres':
return {}
statuses = load_checked_statuses(checked_file)
for status, path in normalize_status_files(status_files).items():
for key in load_keys_from_file(path):
statuses.setdefault(key, status)
return statuses
def cached_occurrence_identity(service, key, source, finding):
finding_hash = finding_secret_hash(finding)
if not finding_hash and isinstance(finding, dict):
finding_hash = sha256_text(json.dumps(finding, ensure_ascii=False, sort_keys=True, default=str))
return sha256_text("|".join([str(service or ""), sha256_text(key), str(source or ""), finding_hash]))
def cached_occurrence_log_path(service=None):
service = service or keycheck_service_name() or "default"
output_dir = os.getenv("KEYCHECK_OUTPUT_DIR") or service_output_dir(service)
require_private_directory(output_dir, create=True)
return os.path.join(output_dir, "cached_occurrences.tsv")
CACHED_OCCURRENCE_CACHE = {}
CACHED_OCCURRENCE_ROWS = {}
CACHED_OCCURRENCE_SIGNATURES = {}
def cached_occurrence_limits():
return {
"items": max(1, env_int("KEYCHECK_CACHED_OCCURRENCE_MAX_ITEMS", 100000)),
"bytes": max(1, env_int("KEYCHECK_CACHED_OCCURRENCE_MAX_BYTES", 16 * 1024 * 1024)),
"ttl": max(60, env_int("KEYCHECK_CACHED_OCCURRENCE_TTL_SEC", 30 * 86400)),
}
def _cached_occurrence_timestamp(value):
try:
parsed = datetime.fromisoformat(str(value or "").replace("Z", "+00:00"))
if parsed.tzinfo is None:
parsed = parsed.replace(tzinfo=timezone.utc)
return parsed.timestamp()
except (TypeError, ValueError):
return 0.0
def _cached_occurrence_signature(path):
try:
details = os.stat(path)
return (int(details.st_size), int(getattr(details, "st_mtime_ns", 0)))
except OSError:
return (0, 0)
def _load_bounded_cached_occurrences(path, limits):
if not os.path.exists(path):
return [], False
size = os.path.getsize(path)
start = max(0, size - limits["bytes"])
rows = []
truncated = start > 0
cutoff = time.time() - limits["ttl"]
expired = False
with open(path, "rb") as handle:
handle.seek(start)
if start:
handle.readline(limits["bytes"] + 1)
payload = handle.read(limits["bytes"] + 1)
if len(payload) > limits["bytes"]:
payload = payload[-limits["bytes"]:]
first_newline = payload.find(b"\n")
payload = payload[first_newline + 1:] if first_newline >= 0 else b""
truncated = True
for raw_line in payload.splitlines():
try:
text = raw_line.decode("utf-8")
except UnicodeDecodeError:
truncated = True
continue
identity, separator, timestamp = text.partition("\t")
identity = identity.strip()
if not identity or not separator:
truncated = True
continue
epoch = _cached_occurrence_timestamp(timestamp.strip())
if epoch < cutoff:
expired = True
continue
rows.append((identity, timestamp.strip(), epoch))
deduped = {}
for identity, timestamp, epoch in rows:
deduped[identity] = (identity, timestamp, epoch)
retained = sorted(deduped.values(), key=lambda item: item[2])[-limits["items"]:]
return retained, truncated or expired or len(retained) != len(rows)
def _save_cached_occurrence_rows(path, rows, byte_limit):
encoded = [f"{identity}\t{timestamp}\n".encode("utf-8") for identity, timestamp, _ in rows]
total = sum(len(item) for item in encoded)
remove = 0
while remove < len(encoded) and total > byte_limit:
total -= len(encoded[remove])
remove += 1
if remove:
encoded = encoded[remove:]
rows = rows[remove:]
with private_atomic_writer(path, binary=True) as handle:
for row in encoded:
handle.write(row)
return rows
def cached_occurrence_set(service=None):
service = service or keycheck_service_name() or "default"
path = cached_occurrence_log_path(service)
signature = _cached_occurrence_signature(path)
cached_rows = CACHED_OCCURRENCE_ROWS.get(service) or []
cache_unexpired = not cached_rows or cached_rows[0][2] >= time.time() - cached_occurrence_limits()["ttl"]
if service in CACHED_OCCURRENCE_CACHE and CACHED_OCCURRENCE_SIGNATURES.get(service) == signature and cache_unexpired:
return CACHED_OCCURRENCE_CACHE[service]
limits = cached_occurrence_limits()
rows, needs_compaction = _load_bounded_cached_occurrences(path, limits)
if needs_compaction:
lock_path = f"{path}.lock"
lock = acquire_file_lock(lock_path, timeout_sec=30)
try:
rows, _ = _load_bounded_cached_occurrences(path, limits)
rows = _save_cached_occurrence_rows(path, rows, limits["bytes"])
finally:
release_file_lock(lock, lock_path)
signature = _cached_occurrence_signature(path)
seen = {identity for identity, _, _ in rows}
CACHED_OCCURRENCE_CACHE[service] = seen
CACHED_OCCURRENCE_ROWS[service] = rows
CACHED_OCCURRENCE_SIGNATURES[service] = signature
return seen
def cached_occurrence_seen(identity, service=None):
return identity in cached_occurrence_set(service)
def mark_cached_occurrence_seen(identity, service=None):
service = service or keycheck_service_name() or "default"
path = cached_occurrence_log_path(service)
lock_path = f"{path}.lock"
lock = acquire_file_lock(lock_path, timeout_sec=30)
try:
limits = cached_occurrence_limits()
signature = _cached_occurrence_signature(path)
if CACHED_OCCURRENCE_SIGNATURES.get(service) == signature:
rows = list(CACHED_OCCURRENCE_ROWS.get(service) or [])
cutoff = time.time() - limits["ttl"]
retained = [row for row in rows if row[2] >= cutoff]
needs_compaction = len(retained) != len(rows)
rows = retained
else:
rows, needs_compaction = _load_bounded_cached_occurrences(path, limits)
seen = {value for value, _, _ in rows}
if identity in seen:
CACHED_OCCURRENCE_CACHE[service] = seen
CACHED_OCCURRENCE_ROWS[service] = rows
CACHED_OCCURRENCE_SIGNATURES[service] = signature
return False
timestamp = now_iso()
rows.append((str(identity), timestamp, time.time()))
line = f"{identity}\t{timestamp}\n".encode("utf-8")
current_size = signature[0]
if (
needs_compaction
or len(rows) > limits["items"]
or current_size + len(line) > limits["bytes"]
):
rows = rows[-limits["items"]:]
rows = _save_cached_occurrence_rows(path, rows, limits["bytes"])
else:
with private_append_writer(path, binary=True) as handle:
handle.write(line)
seen = {value for value, _, _ in rows}
CACHED_OCCURRENCE_CACHE[service] = seen
CACHED_OCCURRENCE_ROWS[service] = rows
CACHED_OCCURRENCE_SIGNATURES[service] = _cached_occurrence_signature(path)
return True
finally:
release_file_lock(lock, lock_path)
def record_cached_keycheck_occurrence(service, key, status, source="", finding=None, detector=None):
if keycheck_input_mode() == 'postgres':
raise RuntimeError('PostgreSQL candidates cannot be completed from cached file state')
if not key or not service or not status:
return False
identity = cached_occurrence_identity(service, key, source, finding)
if cached_occurrence_seen(identity, service):
return False
finding = strip_finding_nearby_context(finding)
secret_hash = finding_secret_hash(finding) or sha256_text(key)
detector_secret_hash = finding_detector_secret_hash(finding)
result = {
"status": str(status).upper(),
"result_source": "cached_status",
"cached_status": True,
"cached_occurrence_id": identity,
"checked_at": now_iso(),
"message": "cached status occurrence; provider API not called",
"key_hash": sha256_text(key),
"secret_hash": secret_hash,
"detector_secret_hash": detector_secret_hash,
"finding_uid": finding_uid(finding),
}
output_dir = os.getenv("KEYCHECK_OUTPUT_DIR") or service_output_dir(service)
require_private_directory(output_dir, create=True)
write_keycheck_event(
service,
os.path.join(output_dir, f"{service}Results.jsonl"),
key,
result,
source,
finding,
detector or get_detector_name(finding or {}) or "",
"cached_status",
)
mark_cached_occurrence_seen(identity, service)
if not truthy_env("KEYCHECK_DB_INLINE", False):
return True
ok = record_validation_result(service, key, result, source, finding, detector)
return ok
def should_skip_key(key, checked_statuses, known_keys, args, retry_statuses=None, service=None, source="", finding=None, detector=None, known_statuses=None):
if keycheck_input_mode() == 'postgres':
if not _ACTIVE_DB_CANDIDATE:
raise RuntimeError('PostgreSQL provider probe has no active fenced candidate')
return False
attempted = getattr(args, "_keycheck_attempted_keys", None)
if not isinstance(attempted, set):
attempted = set()
setattr(args, "_keycheck_attempted_keys", attempted)
attempt_identity = (str(service or keycheck_service_name() or "default").lower(), key)
if attempt_identity in attempted:
if service and source:
cached_status = checked_statuses.get(key) or (known_statuses or {}).get(key) or "UNKNOWN"
record_cached_keycheck_occurrence(service, key, cached_status, source, finding, detector)
return True
if getattr(args, "recheck_all", False):
attempted.add(attempt_identity)
return False
status = checked_statuses.get(key) or (known_statuses or {}).get(key)
retry_statuses = retry_statuses or set()
if status in retry_statuses:
attempted.add(attempt_identity)
return False
if status is None and retry_statuses and key in known_keys:
attempted.add(attempt_identity)
return False
skip = key in known_keys or key in checked_statuses
if skip and service and source:
cached_status = checked_statuses.get(key) or (known_statuses or {}).get(key) or "UNKNOWN"
record_cached_keycheck_occurrence(service, key, cached_status, source, finding, detector)
if not skip:
attempted.add(attempt_identity)
return skip
def load_proxies(proxy_file):
if not os.path.exists(proxy_file):
print(f"Info: {proxy_file} not found. Requests will go directly.")
return None
proxies = []
with open(proxy_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
ip, port, login, password = line.split(":")
proxy_url = f"http://{login}:{password}@{ip}:{port}"
proxies.append({"http": proxy_url, "https": proxy_url})
except ValueError:
print(f"Warning: bad proxy format: {line}. Skipping.")
if not proxies:
print(f"Warning: {proxy_file} is empty. Requests will go directly.")
return None
print(f"Loaded proxies: {len(proxies)}")
return cycle(proxies)
def get_detector_name(data):
if data.get("DetectorName"):
return data.get("DetectorName")
if data.get("detector"):
return data.get("detector")
finding = data.get("finding")
if isinstance(finding, dict):
return finding.get("DetectorName")
return None
def get_raw_values(data):
if data.get("DetectorName"):
return data.get("Raw"), data.get("RawV2"), data
if data.get("detector"):
finding = data.get("finding") if isinstance(data.get("finding"), dict) else data
return data.get("raw"), data.get("raw_v2"), finding
finding = data.get("finding")
if isinstance(finding, dict):
return finding.get("Raw"), finding.get("RawV2"), finding
return None, None, data
def iter_findings(input_file, detector_names):
if keycheck_input_mode() == 'postgres':
global _ACTIVE_DB_CANDIDATE
service = keycheck_service_name()
if not service:
raise RuntimeError('PostgreSQL keycheck input mode requires KEYCHECK_SERVICE')
owner = f'{service}:{os.getpid()}:{uuid.uuid4().hex}'
detector_names = set(detector_names) if detector_names is not None else None
slice_limit = max(0, env_int('KEYCHECK_PROVIDER_SLICE_KEYS', 0))
yielded = 0
try:
while True:
if slice_limit and yielded >= slice_limit:
return
candidate = _postgres_candidate_db().claim_keycheck_candidate(
service, owner,
lease_seconds=max(30, env_int('KEYCHECK_CANDIDATE_LEASE_SEC', 300)),
result_projection_reserve_bytes=max(
3 * 1024 * 1024,
env_int('KEYCHECK_RESULT_PROJECTION_RESERVE_BYTES', 3 * 1024 * 1024),
),
projection_max_items=max(
1, env_int('KEYCHECK_PROJECTION_MAX_ITEMS', 10000),
),
projection_max_bytes=max(
3 * 1024 * 1024,
env_int('KEYCHECK_PROJECTION_MAX_BYTES', 2 * 1024 * 1024 * 1024),
),
)
if not candidate:
return
if candidate.get('_capacity_blocked'):
print(
'PostgreSQL keycheck blocked: projection capacity is saturated',
flush=True,
)
raise SystemExit(KEYCHECK_CAPACITY_BLOCKED_EXIT)
_ACTIVE_DB_CANDIDATE = candidate
yielded += 1
finding = dict(candidate.get('finding') or {})
candidate_metadata = safe_json_loads(candidate.get('metadata_json')) or {}
if not finding:
finding = {
'DetectorName': candidate.get('detector_name') or candidate_metadata.get('detector_name'),
'Raw': candidate.get('secret_text') or '',
'RawV2': candidate_metadata.get('raw_v2') or candidate.get('secret_json') or '',
}
detector = finding.get('DetectorName') or candidate.get('detector_name')
candidate_kind = candidate.get('candidate_kind') or ''
structured_route = candidate_kind not in ('', 'provider_key')
provider_hint = str(candidate_metadata.get('provider_hint') or '').lower()
persisted_route = (
provider_hint == service
or (
service == 'provider_resolver'
and provider_hint in (
AMBIGUOUS_QWEN_DEEPSEEK_HINT, AMBIGUOUS_GENERIC_SK_HINT,
)
)
)
if (
detector_names is not None and detector not in detector_names
and not structured_route and not persisted_route
):
_postgres_candidate_db().quarantine_keycheck_candidate(
candidate['id'], candidate['lease_token'],
'candidate_provider_route_mismatch',
'candidate detector does not match provider route',
)
_ACTIVE_DB_CANDIDATE = None
continue
candidate_db = _postgres_candidate_db()
cached_completed = False
cached_state_retry_exhausted = False
for cached_attempt in range(2):
cached = candidate_db.keycheck_candidate_cached_status(
candidate['id'], candidate['lease_token'],
)
if cached is None:
raise RuntimeError('PostgreSQL cached-status lookup lost its candidate fence')
if cached.get('probe_required'):
break
event_id = sha256_text('|'.join((
'truf-keycheck-cached-event-v1', str(candidate['id']),
)))
cached_metadata = {
'cached_status': True,
'cached_state_version': cached['state_version'],
'cached_result_id': cached['last_result_id'],
'cached_checked_at': cached['checked_at'],
'cached_result_source': cached['result_source'],
}
outcome = candidate_db.complete_keycheck_candidate(
candidate['id'], candidate['lease_token'], event_id,
cached['status'], cached['status_group'], checked_at=now_iso(),
message='cached current status occurrence; provider API not called',
metadata=cached_metadata, result_source='cached_status',
cached_state_version=cached['state_version'],
cached_last_result_id=cached['last_result_id'],
)
if outcome and outcome.get('completed'):
candidate['_completed'] = True
_ACTIVE_DB_CANDIDATE = None
cached_completed = True
break
if outcome and outcome.get('cached_state_changed'):
cached_state_retry_exhausted = cached_attempt == 1
continue
if outcome and outcome.get('probe_required'):
break
raise RuntimeError('PostgreSQL cached-status completion lost its candidate fence')
if cached_completed:
continue
if cached_state_retry_exhausted:
candidate_db.defer_keycheck_candidate(
candidate['id'], candidate['lease_token'],
'current status changed during cached occurrence completion', 1,
)
_ACTIVE_DB_CANDIDATE = None
continue
raw = finding.get('Raw')
raw_v2 = finding.get('RawV2')
if not raw and candidate.get('secret_text'):
raw = candidate['secret_text']
if not raw_v2 and candidate.get('secret_json'):
raw_v2 = candidate['secret_json']
yield {
'line': candidate['id'],
'detector': detector,
'raw': raw or '',
'raw_v2': raw_v2 or '',
'finding': finding,
'source': f'postgres:keycheck_candidates:{candidate["id"]}',
'candidate_id': candidate['id'],
'candidate_kind': candidate_kind,
'credential_secret_text': candidate.get('secret_text') or '',
'credential_secret_json': candidate.get('secret_json') or '',
'credential_endpoint': candidate.get('endpoint') or '',
'credential_principal': candidate.get('principal') or '',
'candidate_metadata': candidate_metadata,
}
if not candidate.get('_completed'):
reason = 'provider iterator resumed without a committed result'
max_unconsumed = max(
1, env_int('KEYCHECK_CANDIDATE_MAX_UNCONSUMED_ATTEMPTS', 3),
)
if int(candidate.get('attempts') or 0) >= max_unconsumed:
_postgres_candidate_db().quarantine_keycheck_candidate(
candidate['id'], candidate['lease_token'],
'provider_candidate_unconsumed', reason,
)
else:
_postgres_candidate_db().defer_keycheck_candidate(
candidate['id'], candidate['lease_token'], reason, 60,
)
_ACTIVE_DB_CANDIDATE = None
finally:
candidate = _ACTIVE_DB_CANDIDATE
if candidate and not candidate.get('_completed'):
try:
_postgres_candidate_db().defer_keycheck_candidate(
candidate['id'], candidate['lease_token'],
'provider iterator closed without a committed result', 60,
)
finally:
_ACTIVE_DB_CANDIDATE = None
return
detector_names = set(detector_names) if detector_names is not None else None
for item in iter_jsonl_input(input_file):
data = item.get("data") or {}
detector = get_detector_name(data)
if detector_names is not None and detector not in detector_names:
continue
raw, raw_v2, finding = get_raw_values(data)
yield {
"line": item.get("line_offset"),
"detector": detector,
"raw": raw or "",
"raw_v2": raw_v2 or "",
"finding": finding,
"source": item.get("source") or input_file,
}
def read_plain_keys(paths, regex):
if keycheck_input_mode() == 'postgres':
return
seen = set()
for path in paths or []:
if not os.path.exists(path):
continue
with open(path, "r", encoding="utf-8") as f:
content = f.read()
for match in regex.findall(content):
key = match if isinstance(match, str) else ":".join(match)
if key not in seen:
seen.add(key)
yield {"source": path, "key": key}
def request_error_message(response):
try:
payload = response.json()
except ValueError:
return response.text[:1000]
return json.dumps(payload, ensure_ascii=False)[:1000]
def classify_common_http_status(status_code):
if status_code in (401, 403):
return "DEAD"
if status_code == 429:
return "LIMITED"
if 500 <= status_code <= 599:
return "NETWORK"
return "UNKNOWN"
def requests_network_error(exc):
return isinstance(exc, requests.RequestException)