Spaces:
Running
Running
deploy(hf): sync szl-holdings/a11oy@main derived COPY set
Browse filesReusable Dockerfile-COPY-derived deploy from szl-holdings/a11oy main.
Files: 1115 Pruned: 0
Derived from Dockerfile COPY sources (NO hand-maintained allowlist).
Signed-off-by: SZL Holdings <noreply@szlholdings.ai>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- szl_connectors/governance.py +70 -12
- szl_connectors/oauth.py +20 -2
- szl_content_address.py +32 -0
- szl_dsse.py +7 -4
szl_connectors/governance.py
CHANGED
|
@@ -10,8 +10,8 @@ DOCTRINE (non-negotiable):
|
|
| 10 |
• Every write emits a DSSE-signed Khipu receipt (real ECDSA-P256 over the DSSE
|
| 11 |
PAE when SZL_COSIGN_PRIVATE_PEM is present; an explicit UNSIGNED envelope
|
| 12 |
otherwise — NEVER a fabricated signature). Reuses the live `szl_dsse` module.
|
| 13 |
-
• No credential value is EVER placed in a receipt body — only a
|
| 14 |
-
|
| 15 |
• State-changing writes carry the 2-person Yuyay gate + Khipu 3-of-4 quorum
|
| 16 |
status (the hatun-mcp governance contract). Until a connector is CONNECTED,
|
| 17 |
write() is refused with an honest reason.
|
|
@@ -22,14 +22,22 @@ call directly.
|
|
| 22 |
"""
|
| 23 |
from __future__ import annotations
|
| 24 |
|
| 25 |
-
import hashlib
|
| 26 |
import json
|
| 27 |
-
import
|
| 28 |
from datetime import datetime, timezone
|
| 29 |
from typing import Any
|
| 30 |
|
|
|
|
|
|
|
| 31 |
# Anti-overconfidence floor: Λ is never reported as 1.0. We cap at this ceiling.
|
| 32 |
LAMBDA_CEILING = 0.985
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
|
| 35 |
def _now() -> str:
|
|
@@ -67,6 +75,53 @@ def quorum_status(present: list[str] | None = None, n: int = 4, f: int = 1) -> d
|
|
| 67 |
}
|
| 68 |
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
def _dsse_sign(payload: dict[str, Any]) -> dict[str, Any]:
|
| 71 |
"""Sign a receipt payload via the live szl_dsse module if importable; else an
|
| 72 |
honest UNSIGNED envelope (NEVER a fabricated signature)."""
|
|
@@ -98,9 +153,10 @@ def receipt_for_write(*, connector_id: str, action: dict[str, Any],
|
|
| 98 |
NOT secret values), the Λ score, quorum status, credential FINGERPRINT HASHES
|
| 99 |
(never the values), and a result summary. Returns {receipt_hash, dsse, body}.
|
| 100 |
"""
|
| 101 |
-
#
|
| 102 |
-
|
| 103 |
-
|
|
|
|
| 104 |
body = {
|
| 105 |
"kind": "szl.connector.write",
|
| 106 |
"connector_id": connector_id,
|
|
@@ -108,13 +164,15 @@ def receipt_for_write(*, connector_id: str, action: dict[str, Any],
|
|
| 108 |
"lambda_value": lambda_value,
|
| 109 |
"lambda_note": "Λ never 1.0 (conformal anti-overconfidence floor 1/(n+1)); Λ = Conjecture 1",
|
| 110 |
"quorum": quorum or quorum_status(),
|
| 111 |
-
"credential_fingerprints": cred_fingerprints
|
| 112 |
-
"result":
|
| 113 |
"emitted_at": _now(),
|
| 114 |
"doctrine": "v11 — Λ-gate + DSSE/Khipu receipt on every write; no committed keys; trust never 100%",
|
| 115 |
}
|
| 116 |
-
|
| 117 |
-
|
|
|
|
|
|
|
| 118 |
body["receipt_hash"] = receipt_hash
|
| 119 |
dsse = _dsse_sign(body)
|
| 120 |
return {"receipt_hash": receipt_hash, "dsse": dsse, "body": body}
|
|
@@ -135,7 +193,7 @@ def gate_write(*, connector_id: str, connected: bool, action: dict[str, Any],
|
|
| 135 |
"""
|
| 136 |
has_method = bool((action or {}).get("method") or (action or {}).get("object")
|
| 137 |
or (action or {}).get("doctype") or (action or {}).get("sobject"))
|
| 138 |
-
leak =
|
| 139 |
q = quorum_status(present=quorum_present)
|
| 140 |
axes = {
|
| 141 |
"connected": 1.0 if connected else 0.0,
|
|
|
|
| 10 |
• Every write emits a DSSE-signed Khipu receipt (real ECDSA-P256 over the DSSE
|
| 11 |
PAE when SZL_COSIGN_PRIVATE_PEM is present; an explicit UNSIGNED envelope
|
| 12 |
otherwise — NEVER a fabricated signature). Reuses the live `szl_dsse` module.
|
| 13 |
+
• No credential value is EVER placed in a receipt body — only a
|
| 14 |
+
PBKDF2-HMAC-SHA256 credential fingerprint.
|
| 15 |
• State-changing writes carry the 2-person Yuyay gate + Khipu 3-of-4 quorum
|
| 16 |
status (the hatun-mcp governance contract). Until a connector is CONNECTED,
|
| 17 |
write() is refused with an honest reason.
|
|
|
|
| 22 |
"""
|
| 23 |
from __future__ import annotations
|
| 24 |
|
|
|
|
| 25 |
import json
|
| 26 |
+
import re
|
| 27 |
from datetime import datetime, timezone
|
| 28 |
from typing import Any
|
| 29 |
|
| 30 |
+
from szl_content_address import sha256_content_address
|
| 31 |
+
|
| 32 |
# Anti-overconfidence floor: Λ is never reported as 1.0. We cap at this ceiling.
|
| 33 |
LAMBDA_CEILING = 0.985
|
| 34 |
+
_FINGERPRINT_RE = re.compile(r"\Apbkdf2-sha256:[0-9a-f]{32}\Z")
|
| 35 |
+
_FINGERPRINT_LABEL_RE = re.compile(r"\A[A-Za-z0-9_.:-]{1,80}\Z")
|
| 36 |
+
_SENSITIVE_KEY_PARTS = frozenset({"password", "passwd", "secret", "token", "credential"})
|
| 37 |
+
_SENSITIVE_KEY_NAMES = frozenset({
|
| 38 |
+
"api_key", "private_key", "authorization", "proxy_authorization",
|
| 39 |
+
"cookie", "set_cookie",
|
| 40 |
+
})
|
| 41 |
|
| 42 |
|
| 43 |
def _now() -> str:
|
|
|
|
| 75 |
}
|
| 76 |
|
| 77 |
|
| 78 |
+
def _is_sensitive_key(key: object) -> bool:
|
| 79 |
+
normalized = re.sub(r"[^a-z0-9]+", "_", str(key).lower()).strip("_")
|
| 80 |
+
parts = frozenset(part for part in normalized.split("_") if part)
|
| 81 |
+
return normalized in _SENSITIVE_KEY_NAMES or bool(parts & _SENSITIVE_KEY_PARTS)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _contains_sensitive_field(value: Any) -> bool:
|
| 85 |
+
if isinstance(value, dict):
|
| 86 |
+
return any(
|
| 87 |
+
_is_sensitive_key(key) or _contains_sensitive_field(child)
|
| 88 |
+
for key, child in value.items()
|
| 89 |
+
)
|
| 90 |
+
if isinstance(value, (list, tuple)):
|
| 91 |
+
return any(_contains_sensitive_field(child) for child in value)
|
| 92 |
+
return False
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def _scrub_sensitive_fields(value: Any) -> Any:
|
| 96 |
+
"""Recursively remove secret-bearing fields without copying their values."""
|
| 97 |
+
if isinstance(value, dict):
|
| 98 |
+
return {
|
| 99 |
+
key: _scrub_sensitive_fields(child)
|
| 100 |
+
for key, child in value.items()
|
| 101 |
+
if not _is_sensitive_key(key)
|
| 102 |
+
}
|
| 103 |
+
if isinstance(value, list):
|
| 104 |
+
return [_scrub_sensitive_fields(child) for child in value]
|
| 105 |
+
if isinstance(value, tuple):
|
| 106 |
+
return tuple(_scrub_sensitive_fields(child) for child in value)
|
| 107 |
+
return value
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def _validated_fingerprints(values: dict[str, str] | None) -> dict[str, str]:
|
| 111 |
+
"""Allow only the KDF output format emitted by ``cred_fingerprint``."""
|
| 112 |
+
safe: dict[str, str] = {}
|
| 113 |
+
for label, fingerprint in (values or {}).items():
|
| 114 |
+
if not isinstance(label, str) or _FINGERPRINT_LABEL_RE.fullmatch(label) is None:
|
| 115 |
+
continue
|
| 116 |
+
if fingerprint == "absent":
|
| 117 |
+
safe[label] = "absent"
|
| 118 |
+
elif isinstance(fingerprint, str) and _FINGERPRINT_RE.fullmatch(fingerprint):
|
| 119 |
+
safe[label] = fingerprint
|
| 120 |
+
else:
|
| 121 |
+
safe[label] = "invalid-fingerprint"
|
| 122 |
+
return safe
|
| 123 |
+
|
| 124 |
+
|
| 125 |
def _dsse_sign(payload: dict[str, Any]) -> dict[str, Any]:
|
| 126 |
"""Sign a receipt payload via the live szl_dsse module if importable; else an
|
| 127 |
honest UNSIGNED envelope (NEVER a fabricated signature)."""
|
|
|
|
| 153 |
NOT secret values), the Λ score, quorum status, credential FINGERPRINT HASHES
|
| 154 |
(never the values), and a result summary. Returns {receipt_hash, dsse, body}.
|
| 155 |
"""
|
| 156 |
+
# Scrub at every nesting level. Direct callers receive the same protection
|
| 157 |
+
# as gate_write callers, including secrets hidden in list/dict children.
|
| 158 |
+
safe_action = _scrub_sensitive_fields(action or {})
|
| 159 |
+
safe_result = _scrub_sensitive_fields(result_summary or {})
|
| 160 |
body = {
|
| 161 |
"kind": "szl.connector.write",
|
| 162 |
"connector_id": connector_id,
|
|
|
|
| 164 |
"lambda_value": lambda_value,
|
| 165 |
"lambda_note": "Λ never 1.0 (conformal anti-overconfidence floor 1/(n+1)); Λ = Conjecture 1",
|
| 166 |
"quorum": quorum or quorum_status(),
|
| 167 |
+
"credential_fingerprints": _validated_fingerprints(cred_fingerprints),
|
| 168 |
+
"result": safe_result,
|
| 169 |
"emitted_at": _now(),
|
| 170 |
"doctrine": "v11 — Λ-gate + DSSE/Khipu receipt on every write; no committed keys; trust never 100%",
|
| 171 |
}
|
| 172 |
+
canonical_body = json.dumps(body, sort_keys=True, separators=(",", ":")).encode()
|
| 173 |
+
receipt_hash = "sha256:" + sha256_content_address(
|
| 174 |
+
canonical_body, purpose="khipu-receipt"
|
| 175 |
+
)
|
| 176 |
body["receipt_hash"] = receipt_hash
|
| 177 |
dsse = _dsse_sign(body)
|
| 178 |
return {"receipt_hash": receipt_hash, "dsse": dsse, "body": body}
|
|
|
|
| 193 |
"""
|
| 194 |
has_method = bool((action or {}).get("method") or (action or {}).get("object")
|
| 195 |
or (action or {}).get("doctype") or (action or {}).get("sobject"))
|
| 196 |
+
leak = _contains_sensitive_field(action or {})
|
| 197 |
q = quorum_status(present=quorum_present)
|
| 198 |
axes = {
|
| 199 |
"connected": 1.0 if connected else 0.0,
|
szl_connectors/oauth.py
CHANGED
|
@@ -91,6 +91,23 @@ PROVIDER_OAUTH: dict[str, dict[str, str]] = {
|
|
| 91 |
},
|
| 92 |
}
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
# Domain-separation salt + work factor for deriving the state-signing key.
|
| 96 |
_STATE_KDF_SALT = b"szl.killinchu.oauth-state.v1"
|
|
@@ -224,18 +241,19 @@ def exchange_code(connector_id: str, *, code: str, state: str, redirect_uri: str
|
|
| 224 |
"provider_detail": str(body)[:200]}
|
| 225 |
refresh = body.get("refresh_token", "")
|
| 226 |
access = body.get("access_token", "")
|
|
|
|
| 227 |
# credential-bound DSSE receipt — fingerprint ONLY, never the token value.
|
| 228 |
from .governance import receipt_for_write
|
| 229 |
rcpt = receipt_for_write(
|
| 230 |
connector_id=connector_id,
|
| 231 |
action={"method": "oauth.credential_bound", "object": "refresh_token",
|
| 232 |
-
"scope":
|
| 233 |
lambda_value=0.9,
|
| 234 |
cred_fingerprints={
|
| 235 |
"refresh_token": cred_fingerprint(refresh) if refresh else "absent",
|
| 236 |
"access_token": cred_fingerprint(access) if access else "absent",
|
| 237 |
},
|
| 238 |
-
result_summary={"granted_scope":
|
| 239 |
"note": "secret persisted to Space secret store only; never committed"},
|
| 240 |
)
|
| 241 |
return {
|
|
|
|
| 91 |
},
|
| 92 |
}
|
| 93 |
|
| 94 |
+
# Keep public scopes in a separate container from endpoint metadata whose
|
| 95 |
+
# ``token`` key is (correctly but over-broadly) treated as sensitive by taint
|
| 96 |
+
# analysis. Receipt content uses this public-only map, so no credential-like
|
| 97 |
+
# container can flow into a protocol content address.
|
| 98 |
+
PROVIDER_SCOPES: dict[str, str] = {
|
| 99 |
+
"salesforce": "api refresh_token",
|
| 100 |
+
"hubspot": "crm.objects.contacts.read crm.objects.companies.read crm.objects.deals.read",
|
| 101 |
+
"zoho_crm": "ZohoCRM.modules.ALL ZohoCRM.org.READ",
|
| 102 |
+
"slack": "channels:read chat:write users:read",
|
| 103 |
+
"okta": "okta.users.read okta.groups.read",
|
| 104 |
+
"entra": "https://graph.microsoft.com/.default offline_access",
|
| 105 |
+
"auth0": "read:users read:logs",
|
| 106 |
+
"dynamics_crm": "https://{org}.api.crm.dynamics.com/.default offline_access",
|
| 107 |
+
"netsuite": "rest_webservices",
|
| 108 |
+
"servicenow": "useraccount",
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
|
| 112 |
# Domain-separation salt + work factor for deriving the state-signing key.
|
| 113 |
_STATE_KDF_SALT = b"szl.killinchu.oauth-state.v1"
|
|
|
|
| 241 |
"provider_detail": str(body)[:200]}
|
| 242 |
refresh = body.get("refresh_token", "")
|
| 243 |
access = body.get("access_token", "")
|
| 244 |
+
public_scope = PROVIDER_SCOPES.get(connector_id, "")
|
| 245 |
# credential-bound DSSE receipt — fingerprint ONLY, never the token value.
|
| 246 |
from .governance import receipt_for_write
|
| 247 |
rcpt = receipt_for_write(
|
| 248 |
connector_id=connector_id,
|
| 249 |
action={"method": "oauth.credential_bound", "object": "refresh_token",
|
| 250 |
+
"scope": public_scope},
|
| 251 |
lambda_value=0.9,
|
| 252 |
cred_fingerprints={
|
| 253 |
"refresh_token": cred_fingerprint(refresh) if refresh else "absent",
|
| 254 |
"access_token": cred_fingerprint(access) if access else "absent",
|
| 255 |
},
|
| 256 |
+
result_summary={"granted_scope": public_scope,
|
| 257 |
"note": "secret persisted to Space secret store only; never committed"},
|
| 258 |
)
|
| 259 |
return {
|
szl_content_address.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
"""Explicit SHA-256 content addressing for protocol and receipt bytes.
|
| 3 |
+
|
| 4 |
+
This module is deliberately *not* a password or credential derivation API.
|
| 5 |
+
Callers must provide one of the narrow protocol purposes below, and the input
|
| 6 |
+
bytes are hashed exactly as supplied. Keeping this operation separate from
|
| 7 |
+
PBKDF2-based credential fingerprints prevents accidental reuse while
|
| 8 |
+
preserving the byte-for-byte hashes already carried by DSSE envelopes and
|
| 9 |
+
Khipu receipts.
|
| 10 |
+
"""
|
| 11 |
+
from __future__ import annotations
|
| 12 |
+
|
| 13 |
+
import hashlib
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
_PURPOSES = frozenset({"dsse-pae", "khipu-receipt", "public-key"})
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def sha256_content_address(data: bytes, *, purpose: str) -> str:
|
| 20 |
+
"""Return the protocol-compatible SHA-256 hex address of public content.
|
| 21 |
+
|
| 22 |
+
``purpose`` is intentionally mandatory and allowlisted. Secret values
|
| 23 |
+
belong in a password KDF such as PBKDF2, never in this function.
|
| 24 |
+
"""
|
| 25 |
+
if purpose not in _PURPOSES:
|
| 26 |
+
raise ValueError(f"unsupported content-address purpose: {purpose!r}")
|
| 27 |
+
if not isinstance(data, bytes):
|
| 28 |
+
raise TypeError("content address input must be bytes")
|
| 29 |
+
return hashlib.sha256(data).hexdigest()
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
__all__ = ["sha256_content_address"]
|
szl_dsse.py
CHANGED
|
@@ -66,13 +66,14 @@ for SZL Khipu receipts, backed by the SZLHOLDINGS **Cosign** keypair.
|
|
| 66 |
from __future__ import annotations
|
| 67 |
|
| 68 |
import base64
|
| 69 |
-
import hashlib
|
| 70 |
import json
|
| 71 |
import os
|
| 72 |
import sys
|
| 73 |
from datetime import datetime, timezone
|
| 74 |
from typing import Any
|
| 75 |
|
|
|
|
|
|
|
| 76 |
KEYID = "szlholdings-cosign"
|
| 77 |
KHIPU_PAYLOAD_TYPE = "application/vnd.szl.khipu+json"
|
| 78 |
COSIGN_PUB_FINGERPRINT_ENV = "SZL_COSIGN_PUB_SHA256" # optional pin
|
|
@@ -151,7 +152,9 @@ def signing_available() -> bool:
|
|
| 151 |
|
| 152 |
|
| 153 |
def public_key_fingerprint() -> str:
|
| 154 |
-
return
|
|
|
|
|
|
|
| 155 |
|
| 156 |
|
| 157 |
# ---------------------------------------------------------------------------
|
|
@@ -171,7 +174,7 @@ def sign_payload(payload_obj: Any, payload_type: str = KHIPU_PAYLOAD_TYPE) -> di
|
|
| 171 |
"payloadType": payload_type,
|
| 172 |
"payload": base64.b64encode(body).decode("ascii"),
|
| 173 |
"_dsse": "DSSEv1",
|
| 174 |
-
"_pae_sha256":
|
| 175 |
"_signed_at": datetime.now(timezone.utc).isoformat(),
|
| 176 |
}
|
| 177 |
priv = _load_private_key()
|
|
@@ -210,7 +213,7 @@ def verify_envelope(env: dict[str, Any]) -> dict[str, Any]:
|
|
| 210 |
return {**out, "verified": False, "reason": "no signatures (unsigned envelope)"}
|
| 211 |
body = base64.b64decode(payload_b64)
|
| 212 |
to_verify = pae(payload_type, body)
|
| 213 |
-
out["pae_sha256"] =
|
| 214 |
pub = _load_public_key()
|
| 215 |
from cryptography.hazmat.primitives.asymmetric import ec
|
| 216 |
from cryptography.hazmat.primitives import hashes
|
|
|
|
| 66 |
from __future__ import annotations
|
| 67 |
|
| 68 |
import base64
|
|
|
|
| 69 |
import json
|
| 70 |
import os
|
| 71 |
import sys
|
| 72 |
from datetime import datetime, timezone
|
| 73 |
from typing import Any
|
| 74 |
|
| 75 |
+
from szl_content_address import sha256_content_address
|
| 76 |
+
|
| 77 |
KEYID = "szlholdings-cosign"
|
| 78 |
KHIPU_PAYLOAD_TYPE = "application/vnd.szl.khipu+json"
|
| 79 |
COSIGN_PUB_FINGERPRINT_ENV = "SZL_COSIGN_PUB_SHA256" # optional pin
|
|
|
|
| 152 |
|
| 153 |
|
| 154 |
def public_key_fingerprint() -> str:
|
| 155 |
+
return sha256_content_address(
|
| 156 |
+
COSIGN_PUBLIC_PEM.strip().encode(), purpose="public-key"
|
| 157 |
+
)
|
| 158 |
|
| 159 |
|
| 160 |
# ---------------------------------------------------------------------------
|
|
|
|
| 174 |
"payloadType": payload_type,
|
| 175 |
"payload": base64.b64encode(body).decode("ascii"),
|
| 176 |
"_dsse": "DSSEv1",
|
| 177 |
+
"_pae_sha256": sha256_content_address(to_sign, purpose="dsse-pae"),
|
| 178 |
"_signed_at": datetime.now(timezone.utc).isoformat(),
|
| 179 |
}
|
| 180 |
priv = _load_private_key()
|
|
|
|
| 213 |
return {**out, "verified": False, "reason": "no signatures (unsigned envelope)"}
|
| 214 |
body = base64.b64decode(payload_b64)
|
| 215 |
to_verify = pae(payload_type, body)
|
| 216 |
+
out["pae_sha256"] = sha256_content_address(to_verify, purpose="dsse-pae")
|
| 217 |
pub = _load_public_key()
|
| 218 |
from cryptography.hazmat.primitives.asymmetric import ec
|
| 219 |
from cryptography.hazmat.primitives import hashes
|