fix(auto-reply): inject timestamp into BodyForAgent for channel messages#41802
fix(auto-reply): inject timestamp into BodyForAgent for channel messages#41802carrotRakko wants to merge 4 commits into
Conversation
Greptile SummaryThis PR fixes a real bug where channel messages (LINE, Telegram, Discord, Slack, etc.) lacked date/time context in Key findings:
Confidence Score: 3/5
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4af43b8a80
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
All three Greptile findings addressed in 70d778f:
✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 70d778f5d0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
70d778f to
4ba11a2
Compare
🔒 Aisle Security AnalysisWe found 2 potential security issue(s) in this PR:
1. 🟡 User-controlled timestamp envelope can spoof/override trusted time context in LLM prompts
Description
Because the detection is purely pattern-based on the untrusted message text, an attacker can craft a message starting with a bracketed date/time (or channel-like envelope) to:
Vulnerable logic (pattern-based trust of untrusted content): const TIMESTAMP_ENVELOPE_PATTERN = /^\[.*\d{4}-\d{2}-\d{2} \d{2}:\d{2}/;
...
if (TIMESTAMP_ENVELOPE_PATTERN.test(message)) {
return message;
}This becomes more impactful after the change because channel messages now also go through RecommendationDo not decide whether to inject trusted time context based solely on user-controlled message text. Safer approaches:
// Always provide trusted time context in a distinct, non-spoofable way
return `Current time (trusted): ${formatted} ${timezone}\n` + message;
return `[${dow} ${formatted}] (trusted) ${message}`;Key point: ensure the model always receives a clearly trusted timestamp that the user cannot suppress or convincingly spoof. 2. 🔵 Timestamp spoofing via bracketed prefix bypassing
|
| Property | Value |
|---|---|
| Severity | Low |
| CWE | CWE-345 |
| Location | src/auto-reply/reply/inbound-context.ts:79-89 |
Description
finalizeInboundContext now injects a timestamp prefix into BodyForAgent for all inbound contexts. However, injectTimestamp will skip injection whenever the message already begins with any bracketed text containing a YYYY-MM-DD HH:MM substring.
This creates a trust-boundary confusion / spoofing issue:
- Input (attacker-controlled):
BodyForAgentcommonly comes from the raw inbound user message (e.g., Discord/Telegram/LINE message text). - Bypass: an attacker can start their message with a fake timestamp prefix like
[Wed 2099-01-01 00:00 UTC] .... - Result:
injectTimestamptreats the attacker text as an existing “system” timestamp envelope and returns it unchanged, so the agent sees the attacker-chosen timestamp (or no true timestamp at all). - Impact: downstream LLM reasoning may treat the prefix as authoritative time context (scheduling, “today” reasoning, time-based policies/workflows, etc.), enabling time-context manipulation.
Vulnerable flow excerpt:
finalizeInboundContextstampsBodyForAgent:
if (normalized.BodyForAgent) {
const stampOpts = { ...opts.timestampOpts };
if (normalized.Timestamp) {
stampOpts.now ??= new Date(normalized.Timestamp);
}
normalized.BodyForAgent = injectTimestamp(normalized.BodyForAgent, stampOpts);
}injectTimestampskips when a leading bracketed timestamp-like string is present:
// Already has an envelope or injected timestamp
if (TIMESTAMP_ENVELOPE_PATTERN.test(message)) {
return message;
}where
const TIMESTAMP_ENVELOPE_PATTERN = /^\[.*\d{4}-\d{2}-\d{2} \d{2}:\d{2}/;Recommendation
Do not treat user text that merely resembles a timestamp envelope as proof that a trusted timestamp is already present.
Safer options:
-
Use a non-spoofable sentinel/structured field for agent time context (preferred):
- Provide timestamp via a structured, trusted metadata channel (e.g., a dedicated system block / separate field) rather than embedding it in user text.
-
If a prefix must be used, make idempotency detection only recognize OpenClaw-generated prefixes (not any bracketed date):
// Example: only skip if the prefix includes an explicit OpenClaw marker.
const OPENCLAW_TS_PREFIX = /^\[OpenClaw time: [A-Za-z]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}[^\]]*\] /;
export function injectTimestamp(message: string, opts?: TimestampInjectionOptions): string {
if (!message.trim()) return message;
// Skip only for OpenClaw-generated timestamps (not user-crafted lookalikes)
if (OPENCLAW_TS_PREFIX.test(message)) return message;
const now = opts?.now ?? new Date();
const timezone = opts?.timezone ?? "UTC";
const formatted = formatZonedTimestamp(now, { timeZone: timezone });
if (!formatted) return message;
const dow = new Intl.DateTimeFormat("en-US", { timeZone: timezone, weekday: "short" }).format(now);
return `[OpenClaw time: ${dow} ${formatted}] ${message}`;
}If you also need to avoid double-stamping channel envelopes, ensure channel envelope formatting uses the same explicit marker, or carry the timestamp out-of-band instead of relying on textual heuristics.
Analyzed PR: #41802 at commit 48c0dcf
Last updated on: 2026-03-22T14:25:11Z
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ba11a23d0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@aisle-research-bot Thanks for the analysis. The idempotency check in However, we consider this outside the scope of this PR for the following reasons:
✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved) |
4ba11a2 to
4ce88d3
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ce88d34f5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Channel messages set `BodyForAgent` to raw text without a timestamp. Agents prioritize `BodyForAgent` over `Body`, so the envelope timestamp in `Body` is never seen by the agent. Models whose providers don't inject date context server-side (e.g. Mistral) hallucinate dates from their training data cutoff. `finalizeInboundContext` now calls `injectTimestamp` on `BodyForAgent` after normalization. This is the shared path that all channels and extensions pass through — one change covers all built-in channels (LINE, Telegram, Discord, Slack, Signal, iMessage, WhatsApp) and all extensions. `injectTimestamp` is idempotent: it skips messages that already have a timestamp envelope or a cron timestamp. Closes openclaw#25334 ✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)
Tests for media understanding (audio/image), Telegram audio transcript, and Discord button clicks now expect the timestamp prefix in BodyForAgent, matching the pattern established in inbound.test.ts. ✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)
- Add timestampOpts to FinalizeInboundContextOptions so callers with config access can pass timezone-aware options to injectTimestamp - Fix missed test assertion in inbound.test.ts (sanitize spoofed markers) - Fix Matrix extension BodyForAgent test to handle timestamp prefix - Update agent-timestamp.ts JSDoc to reflect finalizeInboundContext usage ✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)
4ce88d3 to
58a27d6
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 58a27d6add
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
When channel message processing is delayed (slow media/link understanding), injectTimestamp's new Date() can diverge from the original message time in ctx.Timestamp. Use the event timestamp when available; fall back to processing time otherwise. ✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)
|
Closing this as implemented after Codex review. Current main already gives inbound channel turns timestamp context through the structured inbound-metadata prompt path, so this PR's What I checked:
So I’m closing this as already implemented rather than keeping a duplicate issue open. Review notes: reviewed against bae7b54a85b1; fix evidence: commit 00bd2cf7a376. |
Summary
BodyForAgent. Models whose providers don't inject date context server-side (e.g., Mistral) hallucinate dates.finalizeInboundContext()now calls the existinginjectTimestamp()onBodyForAgent. Tests updated.agent/chat.sendhandlers untouched. System prompt date injection unaffected.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
Agents connected via channels now receive a timestamp in their message context. Models that previously hallucinated dates will now have correct date/time awareness.
Security Impact (required)
Repro + Verification
Environment
Steps
Expected
Actual
Evidence
Tests in
inbound.test.ts,monitor.test.ts,apply.test.ts, andbot-message-context.audio-transcript.test.tsupdated to assert timestamp presence inBodyForAgent.Human Verification (required)
injectTimestampis idempotent — messages already with a timestamp (gateway path, cron) are not double-stamped.finalizeInboundContext) is shared by all.Review Conversations
N/A — fresh PR, no review conversations yet.
Compatibility / Migration
Failure Recovery (if this breaks)
injectTimestampcall ininbound-context.tssrc/auto-reply/reply/inbound-context.tsRisks and Mitigations
injectTimestampis idempotent — checks for existing timestamp envelopes before injecting.✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)