Skip to content

env: unquote doesn't reverse \\\\ escape — backslash values are corrupted on round-trip #3

@oratis

Description

@oratis

Bug

quoteValue and unquote in src/env.ts are asymmetric. When saveConfigEnv writes a value containing a backslash, quoteValue escapes \\\ (line 67). But unquote (line 112) only reverses \"" and \n → newline — it never undoes the \\\ part. The result: any value with backslashes gains an extra \ on every save/load cycle.

Reproduce (no install required):

function quoteValue(value) {
  if (/^[A-Za-z0-9_\-.\/:+@]+$/.test(value)) return value;
  return '"' + value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
}
function unquote(value) {
  if (value.length >= 2 && value[0] === '"' && value[value.length-1] === '"') {
    const inner = value.slice(1, -1);
    return inner.replace(/\\"/g, '"').replace(/\\n/g, '\n');
  }
  return value;
}
// Round-trip:
const original = 'C:\\Users\\path';
console.log(unquote(quoteValue(original)) === original); // → false

Affected real-world values:

  • Windows paths (C:\Users\…) — relevant once we have proper Windows support, but already broken if anyone sets one today via lisa init --api-key … or similar.
  • Any token / key containing a literal backslash (rare but real for some service auth schemes).
  • Anything pasted from copy-paste that includes line continuations or escape characters.

Fix

Rewrite unquote's unescape pass as a single regex with a substitution callback so we undo all three of \\, \", \n in one go (avoiding the multi-pass ordering trap of replace(/\\\\/g, '\\') colliding with \\n):

return inner.replace(/\\(["\\n])/g, (_, c) => (c === "n" ? "\n" : c));

Scope

  • One file: src/env.ts
  • One function: unquote (3-line change)
  • No public API, no new deps. The asymmetry was a latent correctness bug; this restores symmetric round-tripping.

PR incoming.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions