Skip to content

fix: decode keys that are quoted because they contain = - #310

Open
MildlyMeticulous wants to merge 1 commit into
npm:mainfrom
MildlyMeticulous:fix/quoted-keys-with-equals
Open

fix: decode keys that are quoted because they contain =#310
MildlyMeticulous wants to merge 1 commit into
npm:mainfrom
MildlyMeticulous:fix/quoted-keys-with-equals

Conversation

@MildlyMeticulous

Copy link
Copy Markdown

What / Why

encode() and decode() disagree about keys that contain =, so stringifyparse silently corrupts both the key and the value.

safe() already knows such a key needs quoting:

const safe = val => {
  if (typeof val !== 'string' || val.match(/[=\r\n]/) || ...) {
    return JSON.stringify(val)
  }
  ...
}

But decode() matches the key with [^=]+, which cannot cross the quotes:

const re = /^\[([^\]]*)\]\s*$|^([^=]+)(=(.*))?$/i

So the = inside the quoted key is taken as the separator.

Steps to Reproduce

const ini = require('ini')

const obj = { 'a=b': 'c' }
const text = ini.stringify(obj)   // '"a=b"=c\n'   <- correctly quoted
ini.parse(text)

Expected: { 'a=b': 'c' }
Actual: { '"a': 'b"=c' }

Same for any key containing =:

input stringify parse of that
{ 'a=b': 'c' } "a=b"=c { '"a': 'b"=c' }
{ 'key=with=eq': 'v' } "key=with=eq"=v { '"key': 'with=eq"=v' }
{ '=lead': 'v' } "=lead"=v { '"': 'lead"=v' }
{ 'tr=': 'v' } "tr="=v { '"tr': '"=v' }

Keys containing = are not hypothetical — Windows registry exports, connection strings and PATH-style entries all produce them, and #96 is the section-header form of the same root cause.

The fix

Add a fully-quoted string as a first alternative for the key, keeping the group numbering unchanged:

const re = /^\[([^\]]*)\]\s*$|^("(?:\.|[^"\])*"\s*|[^=]+)(=(.*))?$/i

unsafe() already strips the quotes and JSON.parses the result, so nothing else changes.

Because the quoted alternative requires a closing quote, an unterminated quote falls through to [^=]+ and behaves exactly as before — '"a=b' still parses to { '"a': 'b' }. Unquoted keys are untouched: a=b=c still gives { a: 'b=c' }.

Verification

  • test/quoted-keys.js added: round-trip cases, the nested-section case, the unquoted-key case, and the unterminated-quote case.
  • Against the unpatched lib/ini.js the new file produces 12 failing assertions; with the fix all pass.
  • Full suite green (6/6 files), branch + statement + function + line coverage still 100%, npm run lint clean.

encode() already quotes a key containing `=` via safe(), but decode()
matched the key with `[^=]+`, so it split on the `=` inside the quotes.
stringify -> parse silently corrupted both the key and the value:

  ini.parse(ini.stringify({ "a=b": "c" }))  // { "\"a": "b\"=c" }

Allow a fully-quoted string as the key alternative, falling back to the
old `[^=]+` when the quote is unterminated so unquoted keys are unchanged.
@MildlyMeticulous
MildlyMeticulous requested a review from a team as a code owner July 25, 2026 04:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant