@sema-agent/core
Version:
Stateless, task-oriented AI agent core
49 lines (48 loc) • 2.36 kB
JavaScript
import { detectSecret, MEMORY_WRAPPER_TAGS } from "../memory.js";
import { defuseFenceMarkers, sanitizeUntrustedText } from "../untrusted-text.js";
export const MEMORY_FILENAME_SEGMENT_RE = /^[A-Za-z0-9._-]+$/;
export function scanMemoryFileName(relPath) {
for (const segment of relPath.split("/")) {
if (segment === "")
continue;
if (!MEMORY_FILENAME_SEGMENT_RE.test(segment)) {
return {
code: "filename",
reason: `file name contains characters outside the memory whitelist (allowed: letters, digits, ".", "_", "-" per path segment)`,
};
}
}
return undefined;
}
export function scanMemoryWrite(text, opts = {}) {
const findings = [];
if (opts.maxBytes !== undefined) {
const size = Buffer.byteLength(text, "utf8");
if (size > opts.maxBytes) {
findings.push({ code: "too_large", reason: `${size} bytes — over the ${opts.maxBytes}-byte cap; split or trim it (rejected, NOT truncated)` });
}
}
const secretLabel = detectSecret(text);
if (secretLabel !== null) {
findings.push({ code: "secret", reason: `detected ${secretLabel}` });
}
if (sanitizeUntrustedText(text, [...MEMORY_WRAPPER_TAGS]) !== text) {
findings.push({ code: "injection", reason: "contains a framing break-out tag (<system-reminder>/<user_memory> family)" });
}
else if (/^[ \t]*(?:<{3,}|>{3,})/m.test(text) && defuseFenceMarkers(text) !== text) {
findings.push({ code: "injection", reason: "contains an untrusted-fence sentinel at line start (a run of 3+ '<' or '>')" });
}
return findings;
}
export function scanRemediation(code) {
switch (code) {
case "secret":
return "Remove the secret value — store a POINTER to where it lives (env var name, vault path) instead of the value itself.";
case "injection":
return "Remove the markup: memory files must not contain <system-reminder>/<user_memory> tags or runs of 3+ '<'/'>' (they would forge the injection framing when this memory is loaded).";
case "filename":
return 'Rename the file using only letters, digits, ".", "_" and "-" in each path segment.';
case "too_large":
return "Split the content into smaller topic files, or trim it.";
}
}