Files
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

142 lines
4.4 KiB
Python

"""CLI command implementations for ``opensre guardrails``."""
from __future__ import annotations
import json
import click
from platform.guardrails.audit import AuditLogger
from platform.guardrails.engine import GuardrailEngine
from platform.guardrails.rules import get_default_rules_path, load_rules
_STARTER_CONFIG = """\
# OpenSRE Guardrails Configuration
# Rules are evaluated against all text sent to LLMs.
# Actions: redact (mask the value), block (reject the request), audit (log and allow).
rules:
- name: aws_access_key
description: "AWS access key IDs (AKIA...)"
action: redact
patterns:
- "(?:AKIA|ASIA)[A-Z0-9]{16}"
- name: aws_secret_key
description: "AWS secret access keys (40-char base64)"
action: redact
patterns:
- "(?i)aws_secret_access_key[\\\\s=:]+[A-Za-z0-9/+=]{40}"
- name: credit_card
description: "Credit card numbers (13-19 digits with optional separators)"
action: block
patterns:
- "\\\\b\\\\d{4}[- ]?\\\\d{4}[- ]?\\\\d{4}[- ]?\\\\d{4}\\\\b"
- name: private_key
description: "PEM-encoded private keys"
action: block
patterns:
- "-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"
- name: generic_api_token
description: "Common API token patterns in key=value assignments"
action: redact
patterns:
- "(?i)(?:api_key|api_token|auth_token|access_token|secret_key)[\\\\s=:]+[A-Za-z0-9_\\\\-]{20,}"
"""
def cmd_init() -> None:
"""Create a starter guardrails.yml with common patterns."""
rules_path = get_default_rules_path()
if rules_path.exists():
click.echo(f" Guardrails config already exists at {rules_path}")
click.echo(" Use 'opensre guardrails rules' to view current rules.")
return
rules_path.parent.mkdir(parents=True, exist_ok=True)
rules_path.write_text(_STARTER_CONFIG, encoding="utf-8")
click.echo(f" Created starter guardrails config at {rules_path}")
click.echo(" Edit the file to customize rules for your environment.")
click.echo(" Test with: opensre guardrails test 'AKIAIOSFODNN7EXAMPLE'")
def cmd_test(text: str) -> None:
"""Scan sample text and display what would be matched/redacted/blocked."""
rules_path = get_default_rules_path()
if not rules_path.exists():
click.echo(f" No guardrails config found at {rules_path}")
click.echo(" Create the file to enable guardrails.")
return
rules = load_rules(rules_path)
if not rules:
click.echo(" No valid rules loaded from config.")
return
engine = GuardrailEngine(rules)
result = engine.scan(text)
if not result.matches:
click.echo(" No matches found.")
return
for match in result.matches:
click.echo(
f" [{match.action.value.upper()}] {match.rule_name}: matched '{match.matched_text}'"
)
if result.blocked:
click.echo(f"\n BLOCKED by: {', '.join(result.blocking_rules)}")
else:
redact_matches = sorted(
(m for m in result.matches if m.action.value == "redact"),
key=lambda m: m.start,
reverse=True,
)
redacted = text
for match in redact_matches:
redacted = (
redacted[: match.start] + f"[REDACTED:{match.rule_name}]" + redacted[match.end :]
)
click.echo(f"\n Redacted output: {redacted}")
def cmd_audit(*, limit: int = 50) -> None:
"""Print recent audit log entries."""
audit_logger = AuditLogger()
entries = audit_logger.read_entries(limit=limit)
if not entries:
click.echo(" No audit entries found.")
return
for entry in entries:
click.echo(json.dumps(entry))
def cmd_rules() -> None:
"""List all configured guardrail rules."""
rules_path = get_default_rules_path()
if not rules_path.exists():
click.echo(f" No guardrails config at {rules_path}")
return
rules = load_rules(rules_path)
if not rules:
click.echo(" No valid rules found in config.")
return
for rule in rules:
status = "enabled" if rule.enabled else "disabled"
patterns_count = len(rule.patterns)
keywords_count = len(rule.keywords)
click.echo(
f" {rule.name:<30} {rule.action.value:<8} {status:<10} "
f"patterns={patterns_count} keywords={keywords_count}"
)
if rule.description:
click.echo(f" {rule.description}")