Skip to content

security: PATH_TO_CLAUDE_CODE_EXECUTABLE newline injection into GITHUB_PATH #1160

@qozle

Description

@qozle

Summary

installClaudeCode() in src/entrypoints/run.ts appends PATH_TO_CLAUDE_CODE_EXECUTABLE's parent directory into GITHUB_PATH without sanitizing the value first:

const customExecutable = process.env.PATH_TO_CLAUDE_CODE_EXECUTABLE;
if (customExecutable) {
  const claudeDir = dirname(customExecutable);
  if (githubPath) {
    await appendFile(githubPath, `${claudeDir}\n`);  // line 55
  }

GITHUB_PATH is a runner-managed file where each line is a directory to add to PATH for subsequent steps. If PATH_TO_CLAUDE_CODE_EXECUTABLE contains embedded newlines (e.g. /usr/local/bin/claude\n/attacker/path), dirname() passes through the embedded newline, and appendFile writes two lines — injecting an attacker-controlled directory into the runner's PATH.

Impact

Any workflow that sets PATH_TO_CLAUDE_CODE_EXECUTABLE from an untrusted source (e.g. a workflow input, an environment variable populated from PR metadata) could have additional directories injected into the runner PATH for all subsequent steps in that job. This could be used to shadow system binaries.

In practice, PATH_TO_CLAUDE_CODE_EXECUTABLE is typically set by the workflow author, so the immediate risk is low. However, defense-in-depth sanitization is appropriate for anything written to runner environment files.

Proposed fix

Strip control characters (including \n, \r, null bytes) from claudeDir before writing to GITHUB_PATH:

const safePath = claudeDir.replace(/[\x00-\x1f\x7f]/g, "");
await appendFile(githubPath, `${safePath}\n`);

Or throw if the sanitized value differs from the original (fail-closed rather than silently stripping).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingp3Minor bug or general feature request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions