# JSON-leak guard playground

Type a value for `CLAUDE_PROJECT_DIR`. The simulator runs the same checks the real OrchestKit hooks now do — `safeProjectDir()` in `src/hooks/src/lib/paths.ts` and `safeMkdirSync()` in `src/hooks/src/lib/safe-fs.ts`. The bug from this PR was that hook stdout JSON ended up in this env var and got passed straight to `fs.mkdirSync`, creating a literal `{"continue":true,"suppressOutput":true}/` directory at the project root.

CLAUDE_PROJECT_DIR =
JSON leak (the bug) JSON array real path empty relative
What is happening?

Real source (excerpt)

    // src/hooks/src/lib/paths.ts
    function stripJsonLeak(value, fallback = '.') {
      if (!value) return fallback;
      if (value.startsWith('{') || value.startsWith('[')) return fallback;
      return value;
    }

    export function safeProjectDir(candidate) {
      if (candidate && !candidate.startsWith('{') && !candidate.startsWith('[')) {
        return candidate;
      }
      const env = stripJsonLeak(process.env.CLAUDE_PROJECT_DIR, '');
      return env || process.cwd();
    }

    // src/hooks/src/lib/safe-fs.ts — last-line defence
    export function safeMkdirSync(path, options) {
      if (!looksLikePath(path)) {
        process.stderr.write(`[orchestkit] safeMkdirSync refused: ${JSON.stringify(path)}\n`);
        return undefined; // fail closed, do NOT create the directory
      }
      return mkdirSync(path, options);
    }