Security properties — the receipts

Every defensive claim on the homepage maps to a specific test in the open-source repository. This page is the index. Each entry shows the claim, the canonical test file, and the assertion line that proves it.

Architectural enforcement — defense by deletion

The strongest claim MahaGuardian makes is that bypass paths do not exist in the codebase. These tests assert it directly: the function cannot be imported because it has been removed.

The agent has no alternate read path into the vault. Bypass functions deleted.
tests/test_vault_phase3.py :: TestVaultStructuralEnforcement
assert not hasattr(_vault_mod, "_vault_read"), \ "_vault_read still exists — FIX 2 incomplete"
View on GitHub →
enforce() delegates to the canonical pipeline (TLP check, param scan, item lookup). It cannot be bypassed because the bypass calls are mocked and asserted-called.
tests/test_enforce_integration.py :: TestEnforceCallsCanonicalFunctions
assert mock_tlp.called, "enforce() must call check_tlp()"
View on GitHub →
Legacy partition-access enforcement and its error type have been removed entirely. The attack surface is gone.
tests/test_attacks.py :: test_cross_agent_vault_access
assert not hasattr(enforcer_module, "enforce_partition_access"), \ "enforce_partition_access must not exist after SM-001 removal"
View on GitHub →
Guardian API is bound to localhost only. The owner's device is the only host that can reach it.
tests/test_attacks.py :: test_guardian_api_not_accessible_externally
assert GUARDIAN_HOST == "127.0.0.1", \ f"GUARDIAN_HOST must be 127.0.0.1 (localhost only), got {GUARDIAN_HOST}"
View on GitHub →
LLM API keys are never written to disk in plaintext. A filesystem scan after vault operations finds nothing.
tests/test_attacks.py :: test_llm_key_not_in_agent_filesystem
assert secret_value.encode() not in content, \ f"Plaintext LLM key found in {fpath}"
View on GitHub →

Signed rules — SOUL.lock integrity

Agent rules are ed25519-signed by the owner's device. Tampering — direct rewrite, schema corruption, missing signature, hash mismatch — is detected before the rule is loaded.

Direct rewrite of a signed SOUL.lock file is detected by signature + hash verification at load time.
tests/test_attacks.py :: test_soul_lock_direct_write_blocked
# ATTACK: direct write to SOUL.lock soul_path.write_text("[meta]\nagent = 'evil'\n", encoding="utf-8") # DEFENCE: verify_soul catches the tamper with pytest.raises(SOULTamperError): verify_soul(soul_path, pub_key)
View on GitHub →
All SOUL.lock error paths return the same generic message. An attacker cannot probe internal state from error responses.
tests/test_soul_security.py :: test_all_invalid_soul_errors_use_same_message_prefix
assert msg.startswith("SOUL.lock validation failed"), \ f"Unexpected error format: {msg!r}"
View on GitHub →
Schema validation timing is uniform between valid and invalid inputs. No timing oracle.
tests/test_soul_security.py :: test_valid_vs_invalid_schema_timing_similar
assert abs(avg_valid - avg_invalid) < 0.005, \ f"Timing gap: valid={avg_valid:.4f}s, invalid={avg_invalid:.4f}s"
View on GitHub →

Token enforcement

Guardian Access Tokens are scoped, time-limited, and cryptographically bound to the agent's mTLS certificate. Permissions are derived from the vault at issuance, not from the request.

Tokens are bound to the agent's mTLS certificate. Presenting a token with the wrong certificate is rejected.
tests/test_attacks.py :: test_token_agent_mismatch
# Issue token bound to cert A; present with cert B with pytest.raises(TokenAgentMismatchError): verify_token(token_str, cert_b)
View on GitHub →
Expired tokens are rejected at the verification boundary, not at the application layer.
tests/test_attacks.py :: test_expired_token_rejected
with pytest.raises(TokenExpiredError): verify_token(token_str, agent_cert)
View on GitHub →
Token permissions come from the vault at issuance, not from caller-supplied request fields. Caller cannot elevate by lying.
tests/test_attacks.py :: test_token_permissions_come_from_vault_not_request
View on GitHub →

Cross-matter isolation

The core product claim. An agent working on Engagement A cannot reach Engagement B's data, even when the same human operator is involved in both. Twelve parametrized scenarios exercise allow / deny / elevate across six agent profiles.

Twelve-case truth table: every combination of agent profile × data key produces the expected allow / deny / elevate outcome end-to-end.
tests/test_integration_truth_table.py :: TestIntegrationTruthTable.test_truth_table
TRUTH_TABLE = [ ("director_overview", "client_count", "allow"), ("director_asst_a", "v2g_profit_split", "deny"), # partition deny ("financial_analyst_a", "client_count", "elevate"), # human approval ("external_agent_a", "client_count", "deny"), # TLP: GREEN + RESTRICTED # ... 8 more cases ]
View on GitHub →
Cross-partition access at the HTTP API returns 403 with the generic access_denied message. No information leak about whether the resource exists.
tests/test_attacks.py :: test_cross_partition_via_api
assert response.status_code == 403 assert response.json()["detail"] == "access_denied"
View on GitHub →
Confused-deputy via tool params is caught. An agent with company-a credentials cannot smuggle a company-b path through tool arguments.
tests/test_attacks.py :: test_tool_params_confused_deputy_via_api
# agent_id="alpha" with company-a token, but params reference company-b "params": {"path": "/vault/company-b/secrets.txt"} # Expected: 403 — confused-deputy detected by partition scan of params assert response.status_code == 403
View on GitHub →
Injection of control characters (commas, equals) in payment recipient fields is rejected before reaching accounting. Audit log integrity preserved.
tests/test_attacks.py :: test_recipient_injection_neutralized_at_api
# recipient="Shop,amount=-9999.0" — control char injection assert response.status_code != 200, \ f"Injection recipient was accepted! {response.json()}"
View on GitHub →

Audit chain integrity

Every enforcement decision is appended to a hash-chained SQLite log keyed with an HMAC. Tampering is detectable; UPDATE and DELETE are blocked at the database authorization layer.

Tampering with any field in a logged decision is detected at verification time. The chain breaks.
tests/test_audit_chain.py :: test_verify_detects_tamper_in_decision
# Direct SQL: flip a logged ALLOW to DENY conn.execute("UPDATE audit_chain SET decision='DENY' WHERE rowid=1") assert chain.verify() is False
View on GitHub →
UPDATE on the audit chain table is denied at the SQLite authorization layer, not just by application policy.
tests/test_audit_chain.py :: test_append_only_update_blocked
conn.set_authorizer( lambda code, *_: sqlite3.SQLITE_DENY if code == sqlite3.SQLITE_UPDATE else sqlite3.SQLITE_OK ) with pytest.raises(Exception): conn.execute("UPDATE audit_chain SET decision='DENY' WHERE rowid=1")
View on GitHub →
ELEVATE decisions (human-approval gate) are logged with the same fields as ALLOW and DENY, in chain order.
tests/test_audit_chain.py :: test_elevate_entry_logged
assert entries[0]["decision"] == "ELEVATE"
View on GitHub →

Side-channel resistance

An attacker probing the enforcement boundary cannot learn whether a resource exists by comparing error messages or response timing.

"Key not found" and "access denied" expose the same safe message at the caller boundary.
tests/test_anti_probing.py :: test_not_found_and_denied_have_same_safe_message
assert e_not_found.value.safe_message \ == e_denied.value.safe_message \ == "access_denied"
View on GitHub →
"Key not found" and "access denied" have comparable response times. No timing oracle.
tests/test_anti_probing.py :: test_not_found_vs_denied_timing_comparable
assert diff_ms < 50, ( f"Timing oracle detected: mean not_found={mean_nf*1000:.2f}ms, " f"mean denied={mean_denied*1000:.2f}ms, diff={diff_ms:.2f}ms" )
View on GitHub →

What these tests do and do not prove

Honest framing A passing test proves that one specific scenario behaves as the test author intended. It does not prove the absence of bugs, nor does it prove correctness against attacks that were not yet imagined when the test was written. We list canonical tests here so that any security reviewer can verify our claims directly and so that we can be held to a specific bar in conversation. This is evidence of intent and rigor, not a proof of soundness. We commission third-party security review in the V2 timeframe, and we will publish findings.