fix(cli): reject systemd directive injection

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-27 15:36:22 +00:00
1 parent 88408b2884
commit cff904bfd2
2 files changed
+12 -1

No files matched your search

@@ -48,6 +48,14 @@ describe("service definition generation", () => {
expect(unit).toContain('Environment="PAPERCLIP_HOME=/home/$$USER/%%i/.paperclip"');
});
it.each([
["instanceId", { instanceId: "team-a\nExecStartPre=/tmp/attack", shimPath: "/home/alice/.local/bin/paperclipai", homeDir: "/home/alice/.paperclip" }],
["shimPath", { instanceId: "team-a", shimPath: "/home/alice/bin/paperclipai\r\nExecStartPre=/tmp/attack", homeDir: "/home/alice/.paperclip" }],
["homeDir", { instanceId: "team-a", shimPath: "/home/alice/.local/bin/paperclipai", homeDir: "/home/alice/.paperclip\nEnvironment=ATTACK=1" }],
])("rejects line breaks in the systemd %s", (_field, input) => {
expect(() => renderSystemdUnit(input)).toThrow("Systemd service values must not contain line breaks");
});
it("generates a launchd agent with keepalive and instance logs", () => {
const plist = renderLaunchdPlist({ instanceId: "team-a", shimPath: "/Users/alice/.local/bin/paperclipai", homeDir: "/Users/alice/.paperclip", stdoutPath: "/Users/alice/.paperclip/instances/team-a/logs/service.log", stderrPath: "/Users/alice/.paperclip/instances/team-a/logs/service.err.log" });
expect(plist).toContain("ing.paperclip.paperclipai.team-a");
+4 -1
View File
@@ -53,6 +53,9 @@ export const defaultCommandRunner: CommandRunner = async (command, args, options
};
function escapeSystemd(value: string): string {
if (/\r|\n/.test(value)) {
throw new Error("Systemd service values must not contain line breaks");
}
return value
.replaceAll("\\", "\\\\")
.replaceAll('"', '\\"')
@@ -82,7 +85,7 @@ export function launchdServiceName(instanceId: string): string {
export function renderSystemdUnit(input: { instanceId: string; shimPath: string; homeDir: string }): string {
return `[Unit]
Description=Paperclip AI (${input.instanceId})
Description=Paperclip AI (${escapeSystemd(input.instanceId)})
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=5