Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion plugins/codex/scripts/session-lifecycle-hook.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,28 @@ function appendEnvVar(name, value) {
if (!process.env.CLAUDE_ENV_FILE || value == null || value === "") {
return;
}
fs.appendFileSync(process.env.CLAUDE_ENV_FILE, `export ${name}=${shellEscape(value)}\n`, "utf8");

const line = `export ${name}=${shellEscape(value)}`;

let lines = [];

try {
lines = fs
.readFileSync(process.env.CLAUDE_ENV_FILE, "utf8")
.split("\n")
.filter(Boolean)
.filter((l) => !l.startsWith(`export ${name}=`));
} catch {
// File doesn't exist yet.
}

lines.push(line);

fs.writeFileSync(
process.env.CLAUDE_ENV_FILE,
lines.join("\n") + "\n",
"utf8"
Comment on lines +56 to +59

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve concurrent updates to the shared env file

When another SessionStart hook writes to the shared CLAUDE_ENV_FILE after this process reads it but before this call, the full-file overwrite silently discards that hook's exports. Matching hooks can execute concurrently, and this hook performs the vulnerable read-modify-write cycle three times, so users with multiple SessionStart hooks can intermittently lose unrelated environment variables; synchronize the update or retain append-only writes rather than replacing the shared file.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex address that feedback

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

);
}

function cleanupSessionJobs(cwd, sessionId) {
Expand Down