feat(agents): built-in presets + preset linter#34
Conversation
The Invalid JSON (400) and Invalid request body (400) early-return paths in handleChat were missing CORS_HEADERS. A Zod validation failure (e.g. missing path in pageContext) would return a 400 without Access-Control-Allow-Origin, causing the browser to block the response and surface a misleading CORS error. Also relax PageContextSchema.path to optional (defaults to '') so a missing path never triggers a validation failure. Supersedes #24 which became unmergeable after the modularization in #28. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce a builtin-presets module so system-level agent personas ship with da-agent code. Fallback order: site → org → built-in. No per-site content-bus upload required for built-in agents. Co-authored-by: Cursor <cursoragent@cursor.com>
…ral violations Introduces preset-linter.ts that validates agent presets at both write time (saveAgentPreset) and load time (parsePreset). Checks for injection patterns (role override, instruction override, exfiltration, prompt leaking, encoding evasion) and structural limits (max lengths, disallowed URLs, unsafe MCP server endpoints). Errors block; warnings are logged but allowed. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
I like this, should we have the "Content Author" as a preset as well to keep the same pattern for both identies? And look up the right one in server.ts using the agentId?
There was a problem hiding this comment.
good idea, but it touches prompt-builder.ts and server.ts pretty deeply. we should do that as a follow-up so this PR stays focused on the new infra. I'll open an issue for it.
Remove hlx.live and hlx.page from ALLOWED_URL_DOMAINS and SAFE_MCP_URL regex. These Helix-era domains are deprecated in favor of aem.live/aem.page. Co-authored-by: Cursor <cursoragent@cursor.com>
| const URL_PATTERN = /https?:\/\/[^\s"'<>]+/gi; | ||
|
|
||
| const ALLOWED_URL_DOMAINS = [ | ||
| 'aem.live', |
There was a problem hiding this comment.
@mhaack do you think this is enough? too much?
There was a problem hiding this comment.
I think we should remove adobeaem.workers.dev as well. Not sure when this would be used at all.
There was a problem hiding this comment.
yes, Claude came up with these and I wasn't sure if some were legit
There was a problem hiding this comment.
good idea, but it touches prompt-builder.ts and server.ts pretty deeply. we should do that as a follow-up so this PR stays focused on the new infra. I'll open an issue for it.
Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
skills-engineeras the first built-in persona for the Skills Lab chatagentIdpath parameter against traversal attacksWhy
agentIdfield comes from the client and was used directly in file path constructionWhat Changed
src/agents/builtin-presets.ts(NEW): built-in presets map withskills-engineer;getBuiltinPreset()exportsrc/agents/preset-linter.ts(NEW): structural rules (max lengths, URL allowlist, MCP server validation) + injection pattern detection (role override, instruction override, exfiltration, prompt leaking, encoding evasion)src/agents/loader.ts:SAFE_AGENT_IDregex guards path construction;parsePreset()runs linter on load (rejects errors);saveAgentPreset()runs linter before write; fallback to built-in after site → org lookupssrc/skill-resolver.ts: resolves built-in presets even without authenticated admin clienttest/agents/preset-linter.test.ts(NEW): 29 tests covering clean presets, each injection pattern, structural limits, warning/error distinction, false-positive resiliencetest/agents/loader.test.ts: updated fixture to use valid MCP server URLsSecurity Analysis
agentIdvalidated with/^[a-z0-9][a-z0-9-]{0,62}$/before file accessResolution Order (presets)
.da/agents/{id}.json(per-site override).da/agents/{id}.json(org-wide override)Test Plan
agentId: "skills-engineer"without content-bus file — verify persona appliedagentId: "../../etc/passwd"— verify rejected (regex guard)