Skip to content

Commit f73ef51

Browse files
committed
fix(parse): remove backspace control characters from input to be parsed
1 parent 94c7c51 commit f73ef51

File tree

4 files changed

+141
-1
lines changed

4 files changed

+141
-1
lines changed

lib/parse.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
1010
module.exports = function (input, callback) {
1111
var result;
1212
try {
13-
result = JSON.parse(input);
13+
// The input is a JSON string, which may contain control characters
14+
// that should be removed. See LLK/scratch-vm#1077
15+
// So far we've only encountered the backspace control character,
16+
// so remove that specific one before continuing.
17+
// SB2 JSONs and SB3 JSONs have different versions of the
18+
// character serialized (e.g. \u0008 and \b), strip out both versions
19+
result = JSON.parse(input.replace(/\\b|\\u0008/g, ''));
1420
} catch (e) {
1521
return callback(e.toString());
1622
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"objName": "Stage",
3+
"variables": [{
4+
"name": "\u0008b-damage",
5+
"value": 47.600000000000406,
6+
"isPersistent": false
7+
}],
8+
"sounds": [{
9+
"soundName": "pop",
10+
"soundID": 1,
11+
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
12+
"sampleCount": 258,
13+
"rate": 11025,
14+
"format": ""
15+
}],
16+
"costumes": [{
17+
"costumeName": "backdrop1",
18+
"baseLayerID": 3,
19+
"baseLayerMD5": "739b5e2a2435f6e1ec2993791b423146.png",
20+
"bitmapResolution": 1,
21+
"rotationCenterX": 240,
22+
"rotationCenterY": 180
23+
}],
24+
"currentCostumeIndex": 0,
25+
"penLayerMD5": "5c81a336fab8be57adc039a8a2b33ca9.png",
26+
"penLayerID": 0,
27+
"tempoBPM": 60,
28+
"videoAlpha": 0.5,
29+
"children": [{
30+
"objName": "Sprite1",
31+
"scripts": [[178.4,
32+
46.15,
33+
[["whenGreenFlag"],
34+
["setVar:to:", "\u0008b-damage", "2"],
35+
["doForever", [["turnRight:", ["readVariable", "\u0008b-damage"]], ["changeVar:by:", "\u0008b-damage", 0.1]]]]]],
36+
"sounds": [{
37+
"soundName": "meow",
38+
"soundID": 0,
39+
"md5": "83c36d806dc92327b9e7049a565c6bff.wav",
40+
"sampleCount": 18688,
41+
"rate": 22050,
42+
"format": ""
43+
}],
44+
"costumes": [{
45+
"costumeName": "costume1",
46+
"baseLayerID": 1,
47+
"baseLayerMD5": "09dc888b0b7df19f70d81588ae73420e.svg",
48+
"bitmapResolution": 1,
49+
"rotationCenterX": 47,
50+
"rotationCenterY": 55
51+
},
52+
{
53+
"costumeName": "costume2",
54+
"baseLayerID": 2,
55+
"baseLayerMD5": "3696356a03a8d938318876a593572843.svg",
56+
"bitmapResolution": 1,
57+
"rotationCenterX": 47,
58+
"rotationCenterY": 55
59+
}],
60+
"currentCostumeIndex": 0,
61+
"scratchX": -12.550000000000011,
62+
"scratchY": -22,
63+
"scale": 1,
64+
"direction": 105.79999999757408,
65+
"rotationStyle": "normal",
66+
"isDraggable": false,
67+
"indexInLibrary": 1,
68+
"visible": true,
69+
"spriteInfo": {
70+
}
71+
},
72+
{
73+
"target": "Stage",
74+
"cmd": "getVar:",
75+
"param": "\u0008b-damage",
76+
"color": 15629590,
77+
"label": "\u0008b-damage",
78+
"mode": 1,
79+
"sliderMin": 0,
80+
"sliderMax": 100,
81+
"isDiscrete": true,
82+
"x": 5,
83+
"y": 5,
84+
"visible": true
85+
}],
86+
"info": {
87+
"userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/68.0.3440.106 Safari\/537.36",
88+
"swfVersion": "v461",
89+
"spriteCount": 1,
90+
"videoOn": false,
91+
"hasCloudData": false,
92+
"scriptCount": 1,
93+
"flashVersion": "MAC 31,0,0,108",
94+
"projectID": "217845144"
95+
}
96+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"targets":[{"isStage":true,"name":"Stage","variables":{"|fg9Sax{3^M|yQ/@+Pb^-\bb-damage-":["\bb-damage",47.600000000000406]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"assetId":"797b03bdb8cf6ccfc30c0692d533d998","name":"backdrop1","bitmapResolution":2,"md5ext":"797b03bdb8cf6ccfc30c0692d533d998.png","dataFormat":"png","rotationCenterX":480,"rotationCenterY":360}],"sounds":[{"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","name":"pop","dataFormat":"wav","format":"","rate":44100,"sampleCount":1032,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"off","textToSpeechLanguage":null},{"isStage":false,"name":"Sprite1","variables":{},"lists":{},"broadcasts":{},"blocks":{"Fo3;}RZh889lf|v)*Wc8":{"opcode":"event_whenflagclicked","next":"s.no,^PDr@)KV@#Z([zR","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":268,"y":102},"s.no,^PDr@)KV@#Z([zR":{"opcode":"data_setvariableto","next":"s7Tv-QPZwL]v-?_)4Cec","parent":"Fo3;}RZh889lf|v)*Wc8","inputs":{"VALUE":[1,[10,"2"]]},"fields":{"VARIABLE":["\bb-damage","|fg9Sax{3^M|yQ/@+Pb^-\bb-damage-"]},"shadow":false,"topLevel":false},"s7Tv-QPZwL]v-?_)4Cec":{"opcode":"control_forever","next":null,"parent":"s.no,^PDr@)KV@#Z([zR","inputs":{"SUBSTACK":[2,"7/?b1TW-/8UT)P}LqB|t"]},"fields":{},"shadow":false,"topLevel":false},"7/?b1TW-/8UT)P}LqB|t":{"opcode":"motion_turnright","next":"phnMvNrC#eBgG0L!TxRq","parent":"s7Tv-QPZwL]v-?_)4Cec","inputs":{"DEGREES":[3,[12,"\bb-damage","|fg9Sax{3^M|yQ/@+Pb^-\bb-damage-"],[4,10]]},"fields":{},"shadow":false,"topLevel":false},"phnMvNrC#eBgG0L!TxRq":{"opcode":"data_changevariableby","next":null,"parent":"7/?b1TW-/8UT)P}LqB|t","inputs":{"VALUE":[1,[4,0.1]]},"fields":{"VARIABLE":["\bb-damage","|fg9Sax{3^M|yQ/@+Pb^-\bb-damage-"]},"shadow":false,"topLevel":false}},"comments":{},"currentCostume":0,"costumes":[{"assetId":"bcaaa8547a07cfe572c0967ba829e99d","name":"costume1","bitmapResolution":1,"md5ext":"bcaaa8547a07cfe572c0967ba829e99d.svg","dataFormat":"svg","rotationCenterX":47,"rotationCenterY":55},{"assetId":"11d6c5fbd91e433a1b85a00fd9dd43b6","name":"costume2","bitmapResolution":1,"md5ext":"11d6c5fbd91e433a1b85a00fd9dd43b6.svg","dataFormat":"svg","rotationCenterX":47,"rotationCenterY":55}],"sounds":[{"assetId":"83c36d806dc92327b9e7049a565c6bff","name":"meow","dataFormat":"wav","format":"","rate":44100,"sampleCount":37376,"md5ext":"83c36d806dc92327b9e7049a565c6bff.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":-12.550000000000011,"y":-22,"size":100,"direction":105.79999999757408,"draggable":false,"rotationStyle":"all around"}],"monitors":[{"id":"|fg9Sax{3^M|yQ/@+Pb^-\bb-damage-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"\bb-damage"},"spriteName":null,"value":47.600000000000406,"x":5,"y":5,"visible":true,"sliderMin":0,"sliderMax":100,"isDiscrete":true}],"extensions":[],"meta":{"semver":"3.0.0","vm":"0.2.0-prerelease.20190213190040","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36"}}

test/unit/parser.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
var fs = require('fs');
2+
var path = require('path');
13
var test = require('tap').test;
24
var parse = require('../../lib/parse');
35
var data = require('../fixtures/data');
@@ -24,3 +26,38 @@ test('invalid', function (t) {
2426
t.end();
2527
});
2628
});
29+
30+
test('backspace control characters get stripped out in sb2', function (t) {
31+
var backspaceControlSb2 = fs.readFileSync(
32+
path.resolve(__dirname, '../fixtures/data/217845144_sb2_control_char_in_strings_credit_to_Mr-Dave_test.json'));
33+
parse(backspaceControlSb2.toString(), function (err, res) {
34+
t.equal(err, null);
35+
t.type(res, 'object');
36+
37+
// Verify that the object doesn't have a control character in the global variable name
38+
t.equal(Array.isArray(res.variables), true);
39+
t.equal(res.variables.length, 1);
40+
// The parsed string should no longer include a backspace control character
41+
t.equal(res.variables[0].name, 'b-damage');
42+
t.end();
43+
});
44+
});
45+
46+
test('backspace control characters get stripped out in sb3', function (t) {
47+
var backspaceControlSb2 = fs.readFileSync(
48+
path.resolve(__dirname, '../fixtures/data/217845144_sb3_control_char_in_strings_credit_to_Mr-Dave_test.json'));
49+
parse(backspaceControlSb2.toString(), function (err, res) {
50+
t.equal(err, null);
51+
t.type(res, 'object');
52+
53+
// Verify that the object doesn't have a control character in the global variable name
54+
t.equal(Array.isArray(res.targets), true);
55+
t.equal(res.targets.length, 2);
56+
t.type(res.targets[0].variables, 'object');
57+
var backspaceControlVariableId = Object.keys(res.targets[0].variables)[0];
58+
var backspaceControlVariable = res.targets[0].variables[backspaceControlVariableId];
59+
// The parsed string should no longer include a backspace control character
60+
t.equal(backspaceControlVariable[0], 'b-damage');
61+
t.end();
62+
});
63+
});

0 commit comments

Comments
 (0)