mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-10 20:50:52 +08:00
Harden attachment access controls and stabilize the Weixin bridge.
This persists referenced media safely, adds explicit attachment ACL/backfill flows, and fixes the Weixin sidecar so official login state can bridge reliably into oclaw without duplicate replays or dropped replies. Made-with: Cursor
This commit is contained in:
parent
6cfaff06f6
commit
31e3962c74
22 changed files with 1885 additions and 55 deletions
|
|
@ -5,6 +5,7 @@ Persistence is untouched; callers use copies when building model context."""
|
|||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import os
|
||||
from typing import Any
|
||||
from oclaw.platform.files.attachment_assets import AttachmentAssetStore
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ _IMAGE_CONTENT_TYPES = frozenset({"image", "input_image"})
|
|||
_BASE64_PAYLOAD_KEYS = ("data", "image_base64", "base64", "content_base64", "body_base64")
|
||||
# Below this length we keep values (tiny icons / markers).
|
||||
_MIN_B64_CHARS = 200
|
||||
_DEFAULT_MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024
|
||||
|
||||
|
||||
def redact_embedded_image_blobs(obj: Any) -> Any:
|
||||
|
|
@ -89,6 +91,19 @@ def ingest_embedded_image_blobs_as_refs(
|
|||
if raw:
|
||||
blob = _decode_image_bytes(raw)
|
||||
if blob:
|
||||
max_bytes = _max_attachment_bytes()
|
||||
if max_bytes > 0 and len(blob) > max_bytes:
|
||||
redacted = _redact_dict(node)
|
||||
redacted["type"] = _ref_type_for_mime(
|
||||
str(node.get("mime") or node.get("mime_type") or "application/octet-stream"),
|
||||
typ,
|
||||
)
|
||||
redacted["error"] = "attachment_too_large"
|
||||
redacted["max_bytes"] = int(max_bytes)
|
||||
redacted["actual_bytes"] = int(len(blob))
|
||||
redacted.setdefault("name", str(node.get("name") or "attachment"))
|
||||
redacted.setdefault("mime", str(node.get("mime") or node.get("mime_type") or "application/octet-stream"))
|
||||
return redacted
|
||||
idx_seed[0] += 1
|
||||
mime = str(node.get("mime") or node.get("mime_type") or "image/png").strip() or "image/png"
|
||||
ext = _filename_ext_for_mime(mime)
|
||||
|
|
@ -195,4 +210,14 @@ def _safe_int(raw: Any) -> int | None:
|
|||
return None
|
||||
|
||||
|
||||
def _max_attachment_bytes() -> int:
|
||||
raw = str(os.getenv("AIA_MAX_ATTACHMENT_BYTES") or "").strip()
|
||||
if raw.isdigit():
|
||||
n = int(raw)
|
||||
if n <= 0:
|
||||
return 0
|
||||
return min(n, 500 * 1024 * 1024)
|
||||
return _DEFAULT_MAX_ATTACHMENT_BYTES
|
||||
|
||||
|
||||
__all__ = ["redact_embedded_image_blobs", "ingest_embedded_image_blobs_as_refs"]
|
||||
|
|
|
|||
|
|
@ -889,6 +889,28 @@ class ToolExecutor:
|
|||
event_type="tool_result",
|
||||
event_payload={"tool_name": tc.name, "observed_rows": int(observed_rows_this_call)},
|
||||
)
|
||||
try:
|
||||
owner = ctx.store.get_ui_session_owner(session_id=ctx.session_id) or {}
|
||||
tid = str(owner.get("tenant_id") or "").strip()
|
||||
uid = str(owner.get("user_id") or "").strip()
|
||||
atts = msg_row.attachments
|
||||
if tid and uid and atts:
|
||||
raw = json.loads(atts) if isinstance(atts, str) else atts
|
||||
items = raw if isinstance(raw, list) else ([raw] if isinstance(raw, dict) else [])
|
||||
for a in items:
|
||||
if not isinstance(a, dict):
|
||||
continue
|
||||
aid = str(a.get("attachment_id") or "").strip().lower()
|
||||
if aid:
|
||||
ctx.store.link_attachment_acl(
|
||||
tenant_id=tid,
|
||||
user_id=uid,
|
||||
session_id=ctx.session_id,
|
||||
attachment_id=aid,
|
||||
source=f"tool:{str(tc.name or '')}",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
tool_msg_write_ms = int((time.perf_counter() - t_db2) * 1000)
|
||||
tool_messages.append({"role": "tool", "tool_call_id": tc.id, "content": tool_content, "name": tc.name})
|
||||
_trace(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue