Skip to content

Commit

Permalink
fix: make json slicing more robust (#611)
Browse files Browse the repository at this point in the history
fixes #585
closes #599

* better slice json, do not use javascript constants after it

* remove console.log

* fix typo in module name

* removed slice, use accumulated string instead (because slice pos was calculated wrong sometime)

* some codestyle fixes

* Update util.js

* Update util.js

* remove unused variable

* Rely on closing all brackets instead of finding a semi-colon

Plus some changes to the error messages

* Clean up tests for cutting JSON

* remove unnessecary lines

Co-authored-by: Aleksei Zbiniakov <alexey@coccoc.com>
Co-authored-by: Alexey <zbinyakov@gmail.com>
Co-authored-by: TimeForANinja <t.kutscha@yahoo.de>
  • Loading branch information
4 people committed Apr 15, 2020
1 parent 04fd05b commit 58fad33
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ exports.getBasicInfo = async(id, options) => {
const jsonStr = util.between(body, 'ytplayer.config = ', '</script>');
let config;
if (jsonStr) {
config = jsonStr.slice(0, jsonStr.lastIndexOf(';ytplayer.load'));
config = util.cutAfterJSON(jsonStr);
return gotConfig(id, options, additional, config, false);
} else {
// If the video page doesn't work, maybe because it has mature content.
Expand Down
52 changes: 52 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,55 @@ exports.parseAbbreviatedNumber = string => {
}
return null;
};

/**
* Match begin and end braces of input JSON, return only json
*
* @param {String} mixedJson
* @return {String}
*/
exports.cutAfterJSON = mixedJson => {
let open, close;
if (mixedJson[0] === '[') {
open = '[';
close = ']';
} else if (mixedJson[0] === '{') {
open = '{';
close = '}';
}

if (!open) {
throw new Error(`Can't cut unsupported JSON (need to begin with [ or { ) but got: ${mixedJson[0]}`);
}

// States if the loop is currently in a string
let isString = false;

// Current open brackets to be closed
let counter = 0;

let i;
for (i = 0; i < mixedJson.length; i++) {
// Toggle the isString boolean when leaving/entering string
if (mixedJson[i] === '"' && mixedJson[i - 1] !== '\\') {
isString = !isString;
continue;
}
if (isString) continue;

if (mixedJson[i] === open) {
counter++;
} else if (mixedJson[i] === close) {
counter--;
}

// All brackets have been closed, thus end of JSON is reached
if (counter === 0) {
// Return the cut JSON
return mixedJson.substr(0, i + 1);
}
}

// We ran through the whole string and ended up with an unclosed bracket
throw Error("Can't cut unsupported JSON (no matching closing bracket found)");
};
48 changes: 48 additions & 0 deletions test/util-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,4 +434,52 @@ describe('util.addFormatMeta()', () => {
});
});
});
describe('util.cutAfterJSON()', () => {
it('Works with simple JSON', () => {
assert.equal(util.cutAfterJSON('{"a": 1, "b": 1}'), '{"a": 1, "b": 1}');
});
it('Cut extra characters after JSON', () => {
assert.equal(util.cutAfterJSON('{"a": 1, "b": 1}abcd'), '{"a": 1, "b": 1}');
});
it('Tolerant to string constants', () => {
assert.equal(util.cutAfterJSON('{"a": "}1", "b": 1}abcd'), '{"a": "}1", "b": 1}');
});
it('Tolerant to string with escaped quoting', () => {
assert.equal(util.cutAfterJSON('{"a": "\\"}1", "b": 1}abcd'), '{"a": "\\"}1", "b": 1}');
});
it('works with nested', () => {
assert.equal(
util.cutAfterJSON('{"a": "\\"1", "b": 1, "c": {"test": 1}}abcd'),
'{"a": "\\"1", "b": 1, "c": {"test": 1}}',
);
});
it('Works with utf', () => {
assert.equal(
util.cutAfterJSON('{"a": "\\"фыва", "b": 1, "c": {"test": 1}}abcd'),
'{"a": "\\"фыва", "b": 1, "c": {"test": 1}}',
);
});
it('Works with \\\\ in string', () => {
assert.equal(
util.cutAfterJSON('{"a": "\\\\фыва", "b": 1, "c": {"test": 1}}abcd'),
'{"a": "\\\\фыва", "b": 1, "c": {"test": 1}}',
);
});
it('Works with [ as start', () => {
assert.equal(
util.cutAfterJSON('[{"a": 1}, {"b": 2}]abcd'),
'[{"a": 1}, {"b": 2}]',
);
});
it('Returns an error when not beginning with [ or {', () => {
assert.throws(() => {
util.cutAfterJSON('abcd]}');
}, /Can't cut unsupported JSON \(need to begin with \[ or { \) but got: ./);
});
it('Returns an error when missing closing bracket', () => {
assert.throws(() => {
util.cutAfterJSON('{"a": 1,{ "b": 1}');
}, /Can't cut unsupported JSON \(no matching closing bracket found\)/);
});
});
});

0 comments on commit 58fad33

Please sign in to comment.