Description
parseConfigGypi in lib/create-config-gypi.js converts single-quoted GYP strings to JSON by doing a bare global substitution:
config = config.replace(/'/g, '"')
This breaks whenever a single-quoted string itself contains double-quote characters, which is ubiquitous in GYP condition strings like:
After the substitution that becomes:
which is invalid JSON and causes JSON.parse to throw:
SyntaxError: Expected ',' or ']' after array element in JSON at position 31 (line 3 column 12)
Affected versions
Reproduced on v12.3.0 and v13.0.0 (latest). The three-step implementation of parseConfigGypi has been unchanged since the function was introduced.
Reproduction
const { parseConfigGypi } = require('node-gyp/lib/create-config-gypi')
// Any real-world binding.gyp that uses condition strings — e.g. canvas@2.11.2
const raw = require('fs').readFileSync('/path/to/canvas/binding.gyp', 'utf8')
parseConfigGypi(raw) // → SyntaxError
canvas@2.11.2 is a good, publicly available repro: install it with --ignore-scripts, then call parseConfigGypi on its binding.gyp.
Root cause
Step 3 of parseConfigGypi is a naive global ' → " replacement. It needs to be a proper tokenizer that converts single-quoted strings to double-quoted JSON strings while escaping any double-quote characters found inside those strings.
Suggested fix
Replace the global regex with a character-by-character pass:
function parseConfigGypi(raw) {
// 1. Strip # comments
let src = raw.replace(/#[^\n]*/g, '')
// 2. Join Python-style multiline string continuations
src = src.replace(/'\\?\n\s*'/g, '')
// 3. Tokenise: convert single-quoted strings to double-quoted JSON strings,
// escaping any embedded double-quote characters.
let out = ''
let i = 0
while (i < src.length) {
const ch = src[i]
if (ch === "'") {
let s = '"'
i++
while (i < src.length && src[i] !== "'") {
if (src[i] === '"') {
s += '\\"'
} else if (src[i] === '\\') {
s += src[i] + (src[i + 1] || '')
i++
} else {
s += src[i]
}
i++
}
s += '"'
out += s
i++ // skip closing `'`
} else if (ch === '"') {
// Copy already-valid double-quoted strings verbatim
out += ch
i++
while (i < src.length && src[i] !== '"') {
if (src[i] === '\\') { out += src[i] + (src[i + 1] || ''); i += 2 }
else { out += src[i]; i++ }
}
out += src[i] || ''
i++
} else {
out += ch
i++
}
}
// 4. Strip trailing commas (also invalid in JSON, common in GYP)
out = out.replace(/,(\s*[}\]])/g, '$1')
return JSON.parse(out)
}
Description
parseConfigGypiinlib/create-config-gypi.jsconverts single-quoted GYP strings to JSON by doing a bare global substitution:This breaks whenever a single-quoted string itself contains double-quote characters, which is ubiquitous in GYP condition strings like:
['OS=="win"', { … }]After the substitution that becomes:
which is invalid JSON and causes
JSON.parseto throw:Affected versions
Reproduced on v12.3.0 and v13.0.0 (latest). The three-step implementation of
parseConfigGypihas been unchanged since the function was introduced.Reproduction
canvas@2.11.2is a good, publicly available repro: install it with--ignore-scripts, then callparseConfigGypion itsbinding.gyp.Root cause
Step 3 of
parseConfigGypiis a naive global'→"replacement. It needs to be a proper tokenizer that converts single-quoted strings to double-quoted JSON strings while escaping any double-quote characters found inside those strings.Suggested fix
Replace the global regex with a character-by-character pass: