Files
awoooi/apps/api/tests/test_telegram_gateway_error_sanitizer.py
ogt 6cf8429d17
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m32s
CD Pipeline / build-and-deploy (push) Successful in 8m2s
CD Pipeline / post-deploy-checks (push) Successful in 2m22s
fix(automation): enforce durable alert closure
Restore D037 durable incident readback without weakening database failure semantics.

Fence Agent99 dispatch and terminal learning to canonical identities, durable leases, verifier receipts, and reconciler-owned checkpoints.

Drain backup and restore legacy receipts in bounded cohorts with strict transport, claim, projection, and no-false-green controls.

Keep SSH service refusal visible while separating it from broker network-policy reachability.
2026-07-14 09:40:53 +08:00

378 lines
15 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from src.services.telegram_gateway import (
_agent99_actionable_outbound_metadata,
_agent99_alert_from_actionable_outbound,
_agent99_alert_from_ai_automation_card,
_alert_notification_source_envelope_extra,
_merge_outbound_source_envelope_extra,
_outbound_source_envelope,
_sanitize_telegram_error,
format_aiops_signal_alert_card,
)
def test_telegram_gateway_sanitizes_bot_token_url() -> None:
raw = "Client error for https://api.telegram.org/bot123456:SECRET/sendMessage"
sanitized = _sanitize_telegram_error(raw)
assert "SECRET" not in sanitized
assert "bot<redacted>" in sanitized
def test_outbound_source_envelope_keeps_replay_context_without_raw_payload() -> None:
payload = {
"chat_id": "-100123",
"text": (
"ACTION REQUIRED INC-20260513-9B082D "
"<code>7f858956</code> token "
"1234567890:abcdefghijklmnopqrstuvwxyzABCDEFGH"
),
"parse_mode": "HTML",
"reply_markup": {
"inline_keyboard": [
[
{"text": "批准", "callback_data": "approve:approval-id-secret"},
{"text": "詳情", "callback_data": "details:approval-id-secret"},
]
]
},
}
envelope = _outbound_source_envelope("sendMessage", payload)
assert envelope["adapter"] == "legacy_telegram_gateway"
assert envelope["method"] == "sendMessage"
assert envelope["payload_sha256"]
assert envelope["payload_keys"] == ["chat_id", "parse_mode", "reply_markup", "text"]
assert envelope["parse_mode"] == "HTML"
assert envelope["reply_markup"]["button_count"] == 2
assert envelope["reply_markup"]["buttons"][0]["callback_prefix"] == "approve"
assert envelope["reply_markup"]["buttons"][1]["callback_prefix"] == "details"
assert envelope["source_refs"]["incident_ids"] == ["INC-20260513-9B082D"]
assert envelope["source_refs"]["code_refs"] == ["7f858956"]
assert "approval-id-secret" not in str(envelope)
assert "1234567890:" not in str(envelope)
assert "ACTION REQUIRED" not in str(envelope)
def test_outbound_source_envelope_reads_incident_refs_from_buttons() -> None:
payload = {
"chat_id": "-100123",
"text": "ACTION REQUIRED without incident id in visible text",
"reply_markup": {
"inline_keyboard": [
[
{"text": "詳情", "callback_data": "detail:INC-20260525-ABC123"},
{"text": "歷史", "callback_data": "history:INC-20260525-ABC123"},
],
[
{"text": "重診", "callback_data": "reanalyze:INC-20260525-DEF456"},
],
],
},
}
envelope = _outbound_source_envelope("sendMessage", payload)
assert envelope["source_refs"]["incident_ids"] == [
"INC-20260525-ABC123",
"INC-20260525-DEF456",
]
assert envelope["reply_markup"]["buttons"][0]["callback_prefix"] == "detail"
assert "detail:INC-20260525-ABC123" not in str(envelope)
def test_outbound_source_envelope_reads_meta_event_ref_from_text() -> None:
payload = {
"chat_id": "-100123",
"text": (
"⚙️ META SYSTEM\n"
"📋 <code>META-20260525201300</code>\n"
"異常元件AI 自健診異常"
),
"parse_mode": "HTML",
"reply_markup": {
"inline_keyboard": [
[{"text": "靜默", "callback_data": "silence:opaque-secret"}]
],
},
}
envelope = _outbound_source_envelope("sendMessage", payload)
assert envelope["source_refs"]["event_ids"] == ["META-20260525201300"]
assert envelope["source_refs"]["incident_ids"] == []
assert envelope["reply_markup"]["buttons"][0]["callback_prefix"] == "silence"
assert "opaque-secret" not in str(envelope)
def test_outbound_source_envelope_reads_ai_advisory_refs_without_raw_callback() -> None:
payload = {
"chat_id": "-100123",
"text": "Coverage 缺口分析",
"reply_markup": {
"inline_keyboard": [
[
{
"text": "已處理",
"callback_data": (
"ai_advisory_handled:coverage_gap:auto_rule_creation"
),
},
{
"text": "忽略",
"callback_data": (
"ai_advisory_snooze:coverage_gap:auto_rule_creation"
),
},
]
],
},
}
envelope = _outbound_source_envelope("sendMessage", payload)
assert envelope["source_refs"]["event_ids"] == [
"ai_advisory:coverage_gap:auto_rule_creation"
]
assert envelope["source_refs"]["advisory_ids"] == [
"coverage_gap:auto_rule_creation"
]
assert envelope["source_refs"]["alert_ids"] == ["coverage_gap"]
assert envelope["source_refs"]["fingerprints"] == [
"ai_advisory:coverage_gap:auto_rule_creation"
]
assert "ai_advisory_handled:coverage_gap:auto_rule_creation" not in str(envelope)
def test_outbound_source_envelope_marks_wazuh_ai_alert_card_for_awooop_readback() -> None:
raw_alert = """
wazuh_dashboard_api_readback_degraded dashboard agent list disappeared
POST /api/check-stored-api status=429 POST /api/check-api status=500
https://127.0.0.1:55000 is unreachable manager registry readback blocked
full_log=/var/ossec/logs/alerts/alerts.json Authorization: Bearer abcdefghijklmnopqrstuvwxyz
"""
card = format_aiops_signal_alert_card(raw_alert)
payload = {
"chat_id": "-100123",
"text": card,
"parse_mode": "HTML",
}
envelope = _outbound_source_envelope("sendMessage", payload)
card_metadata = envelope["ai_automation_alert_card"]
assert card_metadata["schema_version"] == "ai_automation_alert_card_mirror_v1"
assert card_metadata["card_schema"] == "ai_automation_alert_card_v1"
assert card_metadata["event_type"] == "wazuh_dashboard_api_readback_degraded"
assert card_metadata["lane"] == "siem_observability_readback_degraded"
assert card_metadata["target"] == "wazuh_dashboard_api"
assert card_metadata["gates"] == [
"controlled_playbook_queue",
"runtime_write_gate=controlled",
]
assert card_metadata["controlled_playbook_queue"] is True
assert card_metadata["runtime_write_gate_state"] == "controlled"
assert card_metadata["runtime_write_gate_count"] == 1
assert card_metadata["delivery_receipt_readback_required"] is True
assert envelope["source_refs"]["alert_ids"] == [
"wazuh_dashboard_api_readback_degraded"
]
assert envelope["source_refs"]["fingerprints"] == [
"ai_automation_alert_card:wazuh_dashboard_api_readback_degraded:siem_observability_readback_degraded"
]
assert "127.0.0.1:55000" not in str(envelope)
assert "/var/ossec" not in str(envelope)
assert "abcdefghijkl" not in str(envelope)
def test_backup_restore_card_persists_fail_closed_readback_contract() -> None:
raw_alert = (
'alertname="BackupAggregateRunFailed" host="110" '
'backup_status=0 escrow_missing=5 restic failed '
'Authorization: Bearer abcdefghijklmnopqrstuvwxyz'
)
card = format_aiops_signal_alert_card(raw_alert)
envelope = _outbound_source_envelope(
"sendMessage",
{"chat_id": "-100123", "text": card, "parse_mode": "HTML"},
)
metadata = envelope["ai_automation_alert_card"]
contract = metadata["backup_restore_automation"]
assert metadata["event_type"] == "backup_restore_escrow_signal"
assert metadata["lane"] == "backup_restore_escrow_triage"
assert metadata["controlled_playbook_queue"] is False
assert metadata["runtime_write_gate_count"] == 0
assert metadata["runtime_write_gate_state"] == "evidence_only"
assert metadata["no_false_green"] is True
assert contract["work_item"]["owner"] == "backup_dr"
assert contract["candidate"]["mode"] == "BackupCheck"
assert contract["candidate"]["execution_state"] == "not_started"
assert metadata["declared_runtime_write_gate_count"] == 0
assert metadata["declared_runtime_write_gate_state"] == "closed"
assert metadata["declared_controlled_playbook_queue"] is True
assert contract["contract_origin"] == "generated_for_send"
assert contract["contract_persisted_at_send"] is False
assert contract["contract_provenance"][
"source_contract_present_at_send"
] is False
assert contract["contract_provenance"]["recorded_policy_present"] is False
assert contract["contract_provenance"][
"recorded_immutable_declared_present"
] is False
assert contract["policy"]["policy_version"] == (
"backup_restore_readback_policy_v2"
)
assert contract["receipt"]["status"] == (
"pending_source_message_persistence"
)
assert contract["receipt"]["is_agent99_execution_receipt"] is False
assert contract["verifier"]["status"] == (
"source_message_pending_verifier_not_started"
)
assert contract["verifier"]["agent99_outcome"]["status"] == (
"outcome_source_unavailable"
)
assert contract["runtime_write_allowed"] is False
assert contract["production_write_performed"] is False
assert "runtime_write_gate=0_until_verifier" in metadata["gates"]
assert "abcdefghijklmnopqrstuvwxyz" not in str(envelope)
notification_receipt = _alert_notification_source_envelope_extra(
safe_text=card,
effective_parse_mode="HTML",
reply_markup_present=False,
)["alert_notification"]
assert notification_receipt["controlled_apply_allowed"] is False
assert notification_receipt["manual_default_route_allowed"] is False
agent99_payload = _agent99_alert_from_ai_automation_card(card, source="unit-test")
assert agent99_payload is not None
assert agent99_payload["kind"] == "backup_health"
assert agent99_payload["suggestedMode"] == "BackupCheck"
assert agent99_payload["controlledApply"] is False
assert "BackupCheck readback candidate only" in agent99_payload["instruction"]
assert agent99_payload["awoooi"]["backupRestoreAutomation"] == contract
def test_ai_alert_card_builds_agent99_provider_freshness_signal() -> None:
raw_alert = """
SourceProviderIngestionStale provider_freshness_signal freshness window timeout
Sentry / SigNoz provider freshness last seen missing direct ref verifier readback
Authorization: Bearer abcdefghijklmnopqrstuvwxyz
"""
card = format_aiops_signal_alert_card(raw_alert)
payload = _agent99_alert_from_ai_automation_card(
card,
source="unit-test",
)
assert payload is not None
assert payload["source"] == "awoooi-api-telegram-gateway"
assert str(payload["id"]).startswith(
"awoooi-telegram-alert-card-provider_freshness_signal-"
)
assert payload["kind"] == "provider_freshness_signal"
assert payload["suggestedMode"] == "ProviderFreshness"
assert payload["controlledApply"] is False
assert payload["routing"]["routeSource"] == "structured_kind"
assert payload["awoooi"]["alertCategory"] == "source_provider_freshness_triage"
assert payload["awoooi"]["notificationType"] == "ai_automation_alert_card_v1"
assert "component=telegram_gateway" in payload["labels"]
assert "source=unit-test" in payload["labels"]
assert "provider-freshness" in payload["labels"]
assert "no-false-green" in payload["labels"]
assert "abcdefghijklmnopqrstuvwxyz" not in str(payload)
def test_action_required_cold_start_card_routes_to_agent99_recover() -> None:
card = (
" ACTION REQUIRED | <b>低風險</b>\n"
"──────────────────────\n"
"📋 <code>INC-20260710-5700F7</code>\n"
"🎯 資源:<code>cold-start-gate</code>\n"
"🏷️ 分類:<b>general</b>\n"
"🧭 處置狀態:<b>AI 選擇不執行修復</b>\n"
"🤖 <b>AI 自動化鏈路</b>"
)
metadata = _agent99_actionable_outbound_metadata(card)
payload = _agent99_alert_from_actionable_outbound(card, source="unit-test")
assert metadata is not None
assert metadata["card_schema"] == "awooop_action_required_v1"
assert metadata["incident_id"] == "INC-20260710-5700F7"
assert metadata["target"] == "cold-start-gate"
assert payload is not None
assert payload["kind"] == "host_recovery"
assert payload["suggestedMode"] == "Recover"
assert payload["controlledApply"] is True
assert payload["resolution"]["policy"] == "mode_verifier_can_resolve"
assert payload["awoooi"]["incidentId"] == "INC-20260710-5700F7"
def test_failed_cicd_card_routes_to_agent99_status_with_commit_receipt() -> None:
card = (
"❌ <b>[AWOOOI CI/CD]</b> | build-and-deploy\n"
"📦 AWOOOI 部署失敗\n"
"📋 <code>85159f25</code>\n"
"📝 fix(security): restrict executor SSH egress\n"
"🔗 <a href='https://gitea.example.invalid/run'>Workflow</a>"
)
metadata = _agent99_actionable_outbound_metadata(card)
payload = _agent99_alert_from_actionable_outbound(card, source="unit-test")
assert metadata is not None
assert metadata["card_schema"] == "awoooi_cicd_failure_v1"
assert metadata["commit_sha"] == "85159f25"
assert payload is not None
assert payload["kind"] == "cicd_failure"
assert payload["suggestedMode"] == "Status"
assert payload["controlledApply"] is False
assert payload["resolution"]["policy"] == "external_source_receipt_required"
assert payload["awoooi"]["commitSha"] == "85159f25"
assert "gitea.example.invalid" not in str(payload)
def test_agent99_dispatch_receipt_is_persisted_without_raw_transport_body() -> None:
envelope = _outbound_source_envelope(
"sendMessage",
{
"chat_id": "chat",
"text": (
" ACTION REQUIRED | 低風險\n"
"📋 INC-20260710-5700F7\n"
"🎯 資源cold-start-gate\n"
"🤖 AI 自動化鏈路"
),
},
)
receipt = {
"schema_version": "telegram_agent99_dispatch_receipt_v1",
"status": "accepted_inbox_triggered",
"transport": "relay",
"alert_id": "awoooi-telegram-actionable-awooop_action_required-safehash",
"kind": "host_recovery",
"suggested_mode": "Recover",
"accepted": True,
"inbox_triggered": True,
"stores_raw_response": False,
}
merged = _merge_outbound_source_envelope_extra(
envelope,
{"agent99_dispatch_receipt": receipt},
)
assert merged["agent99_dispatch_receipt"] == receipt
assert merged["agent99_actionable_outbound"]["incident_id"] == (
"INC-20260710-5700F7"
)
assert "incomingFile" not in str(merged)
assert "private-path" not in str(merged)