Increase security and reformat

This commit is contained in:
2026-03-07 20:52:13 +00:00
parent 10588a18b9
commit bca4d6898f
144 changed files with 6735 additions and 3960 deletions

View File

@@ -83,7 +83,9 @@ def ensure_default_source_for_chat(
message=None,
):
service_key = str(service or "").strip().lower()
normalized_identifier = normalize_channel_identifier(service_key, channel_identifier)
normalized_identifier = normalize_channel_identifier(
service_key, channel_identifier
)
variants = channel_variants(service_key, normalized_identifier)
if not service_key or not variants:
return None

View File

@@ -72,9 +72,13 @@ def queue_codex_event_with_pre_approval(
},
)
cfg = TaskProviderConfig.objects.filter(user=user, provider=provider, enabled=True).first()
cfg = TaskProviderConfig.objects.filter(
user=user, provider=provider, enabled=True
).first()
settings_payload = dict(getattr(cfg, "settings", {}) or {})
approver_service = str(settings_payload.get("approver_service") or "").strip().lower()
approver_service = (
str(settings_payload.get("approver_service") or "").strip().lower()
)
approver_identifier = str(settings_payload.get("approver_identifier") or "").strip()
if approver_service and approver_identifier:
try:

View File

@@ -57,7 +57,9 @@ def resolve_external_chat_id(*, user, provider: str, service: str, channel: str)
provider=provider,
enabled=True,
)
.filter(Q(person_identifier=person_identifier) | Q(person=person_identifier.person))
.filter(
Q(person_identifier=person_identifier) | Q(person=person_identifier.person)
)
.order_by("-updated_at", "-id")
.first()
)

View File

@@ -22,16 +22,23 @@ from core.models import (
TaskEpic,
TaskProviderConfig,
)
from core.tasks.chat_defaults import ensure_default_source_for_chat, resolve_message_scope
from core.tasks.codex_approval import queue_codex_event_with_pre_approval
from core.tasks.providers import get_provider
from core.tasks.codex_support import resolve_external_chat_id
from core.security.command_policy import CommandSecurityContext, evaluate_command_policy
from core.tasks.chat_defaults import (
ensure_default_source_for_chat,
resolve_message_scope,
)
from core.tasks.codex_approval import queue_codex_event_with_pre_approval
from core.tasks.codex_support import resolve_external_chat_id
from core.tasks.providers import get_provider
_TASK_HINT_RE = re.compile(r"\b(todo|task|action|need to|please)\b", re.IGNORECASE)
_COMPLETION_RE = re.compile(r"\b(done|completed|fixed)\s*#([A-Za-z0-9_-]+)\b", re.IGNORECASE)
_COMPLETION_RE = re.compile(
r"\b(done|completed|fixed)\s*#([A-Za-z0-9_-]+)\b", re.IGNORECASE
)
_BALANCED_HINT_RE = re.compile(r"\b(todo|task|action item|action)\b", re.IGNORECASE)
_BROAD_HINT_RE = re.compile(r"\b(todo|task|action|need to|please|reminder)\b", re.IGNORECASE)
_BROAD_HINT_RE = re.compile(
r"\b(todo|task|action|need to|please|reminder)\b", re.IGNORECASE
)
_PREFIX_HEAD_TRIM = " \t\r\n`'\"([{<*#-—_>.,:;!/?\\|"
_LIST_TASKS_RE = re.compile(
r"^\s*(?:\.l(?:\s+list(?:\s+tasks?)?)?|\.list(?:\s+tasks?)?)\s*$",
@@ -151,15 +158,23 @@ async def _resolve_source_mappings(message: Message) -> list[ChatTaskSource]:
lookup_service = str(message.source_service or "").strip().lower()
variants = _channel_variants(lookup_service, message.source_chat_id or "")
session_identifier = getattr(getattr(message, "session", None), "identifier", None)
canonical_service = str(getattr(session_identifier, "service", "") or "").strip().lower()
canonical_identifier = str(getattr(session_identifier, "identifier", "") or "").strip()
canonical_service = (
str(getattr(session_identifier, "service", "") or "").strip().lower()
)
canonical_identifier = str(
getattr(session_identifier, "identifier", "") or ""
).strip()
if lookup_service == "web" and canonical_service and canonical_service != "web":
lookup_service = canonical_service
variants = _channel_variants(lookup_service, message.source_chat_id or "")
for expanded in _channel_variants(lookup_service, canonical_identifier):
if expanded and expanded not in variants:
variants.append(expanded)
elif canonical_service and canonical_identifier and canonical_service == lookup_service:
elif (
canonical_service
and canonical_identifier
and canonical_service == lookup_service
):
for expanded in _channel_variants(canonical_service, canonical_identifier):
if expanded and expanded not in variants:
variants.append(expanded)
@@ -170,10 +185,14 @@ async def _resolve_source_mappings(message: Message) -> list[ChatTaskSource]:
if not signal_value:
continue
companions += await sync_to_async(list)(
Chat.objects.filter(source_uuid=signal_value).values_list("source_number", flat=True)
Chat.objects.filter(source_uuid=signal_value).values_list(
"source_number", flat=True
)
)
companions += await sync_to_async(list)(
Chat.objects.filter(source_number=signal_value).values_list("source_uuid", flat=True)
Chat.objects.filter(source_number=signal_value).values_list(
"source_uuid", flat=True
)
)
for candidate in companions:
for expanded in _channel_variants("signal", str(candidate or "").strip()):
@@ -271,7 +290,8 @@ def _normalize_flags(raw: dict | None) -> dict:
row = dict(raw or {})
return {
"derive_enabled": _to_bool(row.get("derive_enabled"), True),
"match_mode": str(row.get("match_mode") or "balanced").strip().lower() or "balanced",
"match_mode": str(row.get("match_mode") or "balanced").strip().lower()
or "balanced",
"require_prefix": _to_bool(row.get("require_prefix"), False),
"allowed_prefixes": _parse_prefixes(row.get("allowed_prefixes")),
"completion_enabled": _to_bool(row.get("completion_enabled"), True),
@@ -287,7 +307,9 @@ def _normalize_partial_flags(raw: dict | None) -> dict:
if "derive_enabled" in row:
out["derive_enabled"] = _to_bool(row.get("derive_enabled"), True)
if "match_mode" in row:
out["match_mode"] = str(row.get("match_mode") or "balanced").strip().lower() or "balanced"
out["match_mode"] = (
str(row.get("match_mode") or "balanced").strip().lower() or "balanced"
)
if "require_prefix" in row:
out["require_prefix"] = _to_bool(row.get("require_prefix"), False)
if "allowed_prefixes" in row:
@@ -304,7 +326,9 @@ def _normalize_partial_flags(raw: dict | None) -> dict:
def _effective_flags(source: ChatTaskSource) -> dict:
project_flags = _normalize_flags(getattr(getattr(source, "project", None), "settings", {}) or {})
project_flags = _normalize_flags(
getattr(getattr(source, "project", None), "settings", {}) or {}
)
source_flags = _normalize_partial_flags(getattr(source, "settings", {}) or {})
merged = dict(project_flags)
merged.update(source_flags)
@@ -360,7 +384,10 @@ async def _derive_title(message: Message) -> str:
{"role": "user", "content": text[:2000]},
]
try:
title = str(await ai_runner.run_prompt(prompt, ai_obj, operation="task_derive_title") or "").strip()
title = str(
await ai_runner.run_prompt(prompt, ai_obj, operation="task_derive_title")
or ""
).strip()
except Exception:
title = ""
return (title or text)[:255]
@@ -376,9 +403,13 @@ async def _derive_title_with_flags(message: Message, flags: dict) -> str:
return (cleaned or title or "Untitled task")[:255]
async def _emit_sync_event(task: DerivedTask, event: DerivedTaskEvent, action: str) -> None:
async def _emit_sync_event(
task: DerivedTask, event: DerivedTaskEvent, action: str
) -> None:
cfg = await sync_to_async(
lambda: TaskProviderConfig.objects.filter(user=task.user, enabled=True).order_by("provider").first()
lambda: TaskProviderConfig.objects.filter(user=task.user, enabled=True)
.order_by("provider")
.first()
)()
provider_name = str(getattr(cfg, "provider", "mock") or "mock")
provider_settings = dict(getattr(cfg, "settings", {}) or {})
@@ -416,7 +447,11 @@ async def _emit_sync_event(task: DerivedTask, event: DerivedTaskEvent, action: s
"source_channel": str(task.source_channel or ""),
"external_chat_id": external_chat_id,
"origin_message_id": str(getattr(task, "origin_message_id", "") or ""),
"trigger_message_id": str(getattr(event, "source_message_id", "") or getattr(task, "origin_message_id", "") or ""),
"trigger_message_id": str(
getattr(event, "source_message_id", "")
or getattr(task, "origin_message_id", "")
or ""
),
"mode": "default",
"payload": event.payload,
"memory_context": memory_context,
@@ -495,7 +530,9 @@ async def _emit_sync_event(task: DerivedTask, event: DerivedTaskEvent, action: s
codex_run.status = status
codex_run.result_payload = dict(result.payload or {})
codex_run.error = str(result.error or "")
await sync_to_async(codex_run.save)(update_fields=["status", "result_payload", "error", "updated_at"])
await sync_to_async(codex_run.save)(
update_fields=["status", "result_payload", "error", "updated_at"]
)
if result.ok and result.external_key and not task.external_key:
task.external_key = str(result.external_key)
await sync_to_async(task.save)(update_fields=["external_key"])
@@ -503,15 +540,28 @@ async def _emit_sync_event(task: DerivedTask, event: DerivedTaskEvent, action: s
async def _completion_regex(message: Message) -> re.Pattern:
patterns = await sync_to_async(list)(
TaskCompletionPattern.objects.filter(user=message.user, enabled=True).order_by("position", "created_at")
TaskCompletionPattern.objects.filter(user=message.user, enabled=True).order_by(
"position", "created_at"
)
)
phrases = [str(row.phrase or "").strip() for row in patterns if str(row.phrase or "").strip()]
phrases = [
str(row.phrase or "").strip()
for row in patterns
if str(row.phrase or "").strip()
]
if not phrases:
phrases = ["done", "completed", "fixed"]
return re.compile(r"\\b(?:" + "|".join(re.escape(p) for p in phrases) + r")\\s*#([A-Za-z0-9_-]+)\\b", re.IGNORECASE)
return re.compile(
r"\\b(?:"
+ "|".join(re.escape(p) for p in phrases)
+ r")\\s*#([A-Za-z0-9_-]+)\\b",
re.IGNORECASE,
)
async def _send_scope_message(source: ChatTaskSource, message: Message, text: str) -> None:
async def _send_scope_message(
source: ChatTaskSource, message: Message, text: str
) -> None:
await send_message_raw(
source.service or message.source_service or "web",
source.channel_identifier or message.source_chat_id or "",
@@ -521,7 +571,9 @@ async def _send_scope_message(source: ChatTaskSource, message: Message, text: st
)
async def _handle_scope_task_commands(message: Message, sources: list[ChatTaskSource], text: str) -> bool:
async def _handle_scope_task_commands(
message: Message, sources: list[ChatTaskSource], text: str
) -> bool:
if not sources:
return False
body = str(text or "").strip()
@@ -538,7 +590,9 @@ async def _handle_scope_task_commands(message: Message, sources: list[ChatTaskSo
.order_by("-created_at")[:20]
)
if not open_rows:
await _send_scope_message(source, message, "[task] no open tasks in this chat.")
await _send_scope_message(
source, message, "[task] no open tasks in this chat."
)
return True
lines = ["[task] open tasks:"]
for row in open_rows:
@@ -573,7 +627,9 @@ async def _handle_scope_task_commands(message: Message, sources: list[ChatTaskSo
.first()
)()
if task is None:
await _send_scope_message(source, message, "[task] nothing to undo in this chat.")
await _send_scope_message(
source, message, "[task] nothing to undo in this chat."
)
return True
ref = str(task.reference_code or "")
title = str(task.title or "")
@@ -596,10 +652,16 @@ async def _handle_scope_task_commands(message: Message, sources: list[ChatTaskSo
.first()
)()
if task is None:
await _send_scope_message(source, message, f"[task] #{reference} not found.")
await _send_scope_message(
source, message, f"[task] #{reference} not found."
)
return True
due_str = f"\ndue: {task.due_date}" if task.due_date else ""
assignee_str = f"\nassignee: {task.assignee_identifier}" if task.assignee_identifier else ""
assignee_str = (
f"\nassignee: {task.assignee_identifier}"
if task.assignee_identifier
else ""
)
detail = (
f"[task] #{task.reference_code}: {task.title}"
f"\nstatus: {task.status_snapshot}"
@@ -624,7 +686,9 @@ async def _handle_scope_task_commands(message: Message, sources: list[ChatTaskSo
.first()
)()
if task is None:
await _send_scope_message(source, message, f"[task] #{reference} not found.")
await _send_scope_message(
source, message, f"[task] #{reference} not found."
)
return True
task.status_snapshot = "completed"
await sync_to_async(task.save)(update_fields=["status_snapshot"])
@@ -633,10 +697,16 @@ async def _handle_scope_task_commands(message: Message, sources: list[ChatTaskSo
event_type="completion_marked",
actor_identifier=str(message.sender_uuid or ""),
source_message=message,
payload={"marker": reference, "command": ".task complete", "via": "chat_command"},
payload={
"marker": reference,
"command": ".task complete",
"via": "chat_command",
},
)
await _emit_sync_event(task, event, "complete")
await _send_scope_message(source, message, f"[task] completed #{task.reference_code}: {task.title}")
await _send_scope_message(
source, message, f"[task] completed #{task.reference_code}: {task.title}"
)
return True
return False
@@ -656,7 +726,9 @@ def _strip_epic_token(text: str) -> str:
return re.sub(r"\s{2,}", " ", cleaned).strip()
async def _handle_epic_create_command(message: Message, sources: list[ChatTaskSource], text: str) -> bool:
async def _handle_epic_create_command(
message: Message, sources: list[ChatTaskSource], text: str
) -> bool:
match = _EPIC_CREATE_RE.match(str(text or ""))
if not match or not sources:
return False
@@ -766,13 +838,21 @@ async def process_inbound_task_intelligence(message: Message) -> None:
if not submit_decision.allowed:
return
completion_allowed = any(bool(_effective_flags(source).get("completion_enabled")) for source in sources)
completion_allowed = any(
bool(_effective_flags(source).get("completion_enabled")) for source in sources
)
completion_rx = await _completion_regex(message) if completion_allowed else None
marker_match = (completion_rx.search(text) if completion_rx else None) or (_COMPLETION_RE.search(text) if completion_allowed else None)
marker_match = (completion_rx.search(text) if completion_rx else None) or (
_COMPLETION_RE.search(text) if completion_allowed else None
)
if marker_match:
ref_code = str(marker_match.group(marker_match.lastindex or 1) or "").strip()
task = await sync_to_async(
lambda: DerivedTask.objects.filter(user=message.user, reference_code=ref_code).order_by("-created_at").first()
lambda: DerivedTask.objects.filter(
user=message.user, reference_code=ref_code
)
.order_by("-created_at")
.first()
)()
if not task:
# parser warning event attached to a newly derived placeholder in mapped project
@@ -848,7 +928,11 @@ async def process_inbound_task_intelligence(message: Message) -> None:
status_snapshot="open",
due_date=parsed_due_date,
assignee_identifier=parsed_assignee,
immutable_payload={"origin_text": text, "task_text": task_text, "flags": flags},
immutable_payload={
"origin_text": text,
"task_text": task_text,
"flags": flags,
},
)
event = await sync_to_async(DerivedTaskEvent.objects.create)(
task=task,

View File

@@ -40,13 +40,21 @@ class ClaudeCLITaskProvider(TaskProvider):
return True
if "unrecognized subcommand 'create'" in text and "usage: claude" in text:
return True
if "unrecognized subcommand 'append_update'" in text and "usage: claude" in text:
if (
"unrecognized subcommand 'append_update'" in text
and "usage: claude" in text
):
return True
if "unrecognized subcommand 'mark_complete'" in text and "usage: claude" in text:
if (
"unrecognized subcommand 'mark_complete'" in text
and "usage: claude" in text
):
return True
return False
def _builtin_stub_result(self, op: str, payload: dict, stderr: str) -> ProviderResult:
def _builtin_stub_result(
self, op: str, payload: dict, stderr: str
) -> ProviderResult:
mode = str(payload.get("mode") or "default").strip().lower()
external_key = (
str(payload.get("external_key") or "").strip()
@@ -117,7 +125,10 @@ class ClaudeCLITaskProvider(TaskProvider):
cwd=workspace if workspace else None,
)
stderr_probe = str(completed.stderr or "").lower()
if completed.returncode != 0 and "unexpected argument '--op'" in stderr_probe:
if (
completed.returncode != 0
and "unexpected argument '--op'" in stderr_probe
):
completed = subprocess.run(
fallback_cmd,
capture_output=True,
@@ -133,7 +144,9 @@ class ClaudeCLITaskProvider(TaskProvider):
payload={"op": op, "timeout_seconds": command_timeout},
)
except Exception as exc:
return ProviderResult(ok=False, error=f"claude_cli_exec_error:{exc}", payload={"op": op})
return ProviderResult(
ok=False, error=f"claude_cli_exec_error:{exc}", payload={"op": op}
)
stdout = str(completed.stdout or "").strip()
stderr = str(completed.stderr or "").strip()
@@ -172,7 +185,12 @@ class ClaudeCLITaskProvider(TaskProvider):
out_payload.update(parsed)
if (not ok) and self._is_task_sync_contract_mismatch(stderr):
return self._builtin_stub_result(op, dict(payload or {}), stderr)
return ProviderResult(ok=ok, external_key=ext, error=("" if ok else stderr[:4000]), payload=out_payload)
return ProviderResult(
ok=ok,
external_key=ext,
error=("" if ok else stderr[:4000]),
payload=out_payload,
)
def healthcheck(self, config: dict) -> ProviderResult:
command = self._command(config)
@@ -193,7 +211,11 @@ class ClaudeCLITaskProvider(TaskProvider):
"stdout": str(completed.stdout or "").strip()[:1000],
"stderr": str(completed.stderr or "").strip()[:1000],
},
error=("" if completed.returncode == 0 else str(completed.stderr or "").strip()[:1000]),
error=(
""
if completed.returncode == 0
else str(completed.stderr or "").strip()[:1000]
),
)
def create_task(self, config: dict, payload: dict) -> ProviderResult:

View File

@@ -46,7 +46,9 @@ class CodexCLITaskProvider(TaskProvider):
return True
return False
def _builtin_stub_result(self, op: str, payload: dict, stderr: str) -> ProviderResult:
def _builtin_stub_result(
self, op: str, payload: dict, stderr: str
) -> ProviderResult:
mode = str(payload.get("mode") or "default").strip().lower()
external_key = (
str(payload.get("external_key") or "").strip()
@@ -117,7 +119,10 @@ class CodexCLITaskProvider(TaskProvider):
cwd=workspace if workspace else None,
)
stderr_probe = str(completed.stderr or "").lower()
if completed.returncode != 0 and "unexpected argument '--op'" in stderr_probe:
if (
completed.returncode != 0
and "unexpected argument '--op'" in stderr_probe
):
completed = subprocess.run(
fallback_cmd,
capture_output=True,
@@ -133,7 +138,9 @@ class CodexCLITaskProvider(TaskProvider):
payload={"op": op, "timeout_seconds": command_timeout},
)
except Exception as exc:
return ProviderResult(ok=False, error=f"codex_cli_exec_error:{exc}", payload={"op": op})
return ProviderResult(
ok=False, error=f"codex_cli_exec_error:{exc}", payload={"op": op}
)
stdout = str(completed.stdout or "").strip()
stderr = str(completed.stderr or "").strip()
@@ -172,7 +179,12 @@ class CodexCLITaskProvider(TaskProvider):
out_payload.update(parsed)
if (not ok) and self._is_task_sync_contract_mismatch(stderr):
return self._builtin_stub_result(op, dict(payload or {}), stderr)
return ProviderResult(ok=ok, external_key=ext, error=("" if ok else stderr[:4000]), payload=out_payload)
return ProviderResult(
ok=ok,
external_key=ext,
error=("" if ok else stderr[:4000]),
payload=out_payload,
)
def healthcheck(self, config: dict) -> ProviderResult:
command = self._command(config)
@@ -193,7 +205,11 @@ class CodexCLITaskProvider(TaskProvider):
"stdout": str(completed.stdout or "").strip()[:1000],
"stderr": str(completed.stderr or "").strip()[:1000],
},
error=("" if completed.returncode == 0 else str(completed.stderr or "").strip()[:1000]),
error=(
""
if completed.returncode == 0
else str(completed.stderr or "").strip()[:1000]
),
)
def create_task(self, config: dict, payload: dict) -> ProviderResult:

View File

@@ -12,14 +12,30 @@ class MockTaskProvider(TaskProvider):
return ProviderResult(ok=True, payload={"provider": self.name})
def create_task(self, config: dict, payload: dict) -> ProviderResult:
ext = str(payload.get("external_key") or "") or f"mock-{int(time.time() * 1000)}"
return ProviderResult(ok=True, external_key=ext, payload={"action": "create_task"})
ext = (
str(payload.get("external_key") or "") or f"mock-{int(time.time() * 1000)}"
)
return ProviderResult(
ok=True, external_key=ext, payload={"action": "create_task"}
)
def append_update(self, config: dict, payload: dict) -> ProviderResult:
return ProviderResult(ok=True, external_key=str(payload.get("external_key") or ""), payload={"action": "append_update"})
return ProviderResult(
ok=True,
external_key=str(payload.get("external_key") or ""),
payload={"action": "append_update"},
)
def mark_complete(self, config: dict, payload: dict) -> ProviderResult:
return ProviderResult(ok=True, external_key=str(payload.get("external_key") or ""), payload={"action": "mark_complete"})
return ProviderResult(
ok=True,
external_key=str(payload.get("external_key") or ""),
payload={"action": "mark_complete"},
)
def link_task(self, config: dict, payload: dict) -> ProviderResult:
return ProviderResult(ok=True, external_key=str(payload.get("external_key") or ""), payload={"action": "link_task"})
return ProviderResult(
ok=True,
external_key=str(payload.get("external_key") or ""),
payload={"action": "link_task"},
)