Skip to content

Commit 01204dd

Browse files
Merge pull request #245 from scratchfoundation/UEPR-230
feat: [UEPR-230] add sanitization when loading a project
2 parents 89c071b + ff75c31 commit 01204dd

File tree

11 files changed

+35
-34
lines changed

11 files changed

+35
-34
lines changed

packages/scratch-vm/src/serialization/deserialize-assets.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const JSZip = require('jszip');
22
const log = require('../util/log');
3+
const {sanitizeSvg} = require('@scratch/scratch-svg-renderer');
34

45
/**
56
* Deserializes sound from file into storage cache so that it can
@@ -156,6 +157,11 @@ const deserializeCostume = function (costume, runtime, zip, assetFileName, textL
156157

157158
return Promise.all([textLayerFilePromise,
158159
costumeFile.async('uint8array')
160+
.then(data =>
161+
(costumeFormat === 'svg' ?
162+
sanitizeSvg.sanitizeByteStream(data) :
163+
data)
164+
)
159165
.then(data => storage.createAsset(
160166
assetType,
161167
// TODO eventually we want to map non-png's to their actual file types?
-941 Bytes
Binary file not shown.
-639 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.

packages/scratch-vm/test/integration/monitors_sb2_to_sb3.js

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,25 @@ const makeTestStorage = require('../fixtures/make-test-storage');
44
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
55
const VirtualMachine = require('../../src/index');
66

7-
let vm;
7+
const projectUri = path.resolve(__dirname, '../fixtures/monitors.sb2');
8+
const project = readFileToBuffer(projectUri);
89

9-
tap.beforeEach(() => {
10-
const projectUri = path.resolve(__dirname, '../fixtures/monitors.sb2');
11-
const project = readFileToBuffer(projectUri);
12-
13-
vm = new VirtualMachine();
14-
vm.attachStorage(makeTestStorage());
15-
16-
// TODO figure out why running threads doesn't work in this test
17-
// vm.start();
18-
vm.clear();
19-
vm.setCompatibilityMode(false);
20-
vm.setTurboMode(false);
21-
22-
return vm.loadProject(project);
23-
});
2410
const test = tap.test;
2511

2612
test('saving and loading sb2 project with monitors preserves sliderMin and sliderMax', t => {
13+
const vm = new VirtualMachine();
14+
vm.attachStorage(makeTestStorage());
2715

2816
vm.on('playgroundData', e /* eslint-disable-line no-unused-vars */ => {
29-
// TODO related to above TODO, comment these back in when we figure out
30-
// why running threads doesn't work with this test
31-
32-
// const threads = JSON.parse(e.threads);
17+
const threads = JSON.parse(e.threads);
3318
// All monitors should create threads that finish during the step and
3419
// are revoved from runtime.threads.
35-
// t.equal(threads.length, 0);
20+
t.equal(threads.length, 0);
3621

3722
// we care that the last step updated the right number of monitors
3823
// we don't care whether the last step ran other threads or not
39-
// const lastStepUpdatedMonitorThreads = vm.runtime._lastStepDoneThreads.filter(thread => thread.updateMonitor);
40-
// t.equal(lastStepUpdatedMonitorThreads.length, 8);
24+
const lastStepUpdatedMonitorThreads = vm.runtime._lastStepDoneThreads.filter(thread => thread.updateMonitor);
25+
t.equal(lastStepUpdatedMonitorThreads.length, 8);
4126

4227
// There should be one additional hidden monitor that is in the monitorState but
4328
// does not start a thread.
@@ -139,13 +124,18 @@ test('saving and loading sb2 project with monitors preserves sliderMin and slide
139124
t.equal(monitorRecord.spriteName, null);
140125
t.equal(monitorRecord.targetId, null);
141126

127+
vm.quit();
142128
t.end();
143129
});
144130

145131
// Start VM, load project, and run
146132
t.doesNotThrow(() => {
147-
const sb3ProjectJson = vm.toJSON();
148-
return vm.loadProject(sb3ProjectJson).then(() => {
133+
vm.start();
134+
vm.clear();
135+
vm.setCompatibilityMode(false);
136+
vm.setTurboMode(false);
137+
return vm.loadProject(project).then(() => {
138+
vm.greenFlag();
149139
setTimeout(() => {
150140
vm.getPlaygroundData();
151141
vm.stopAll();

packages/scratch-vm/test/integration/offline-custom-assets.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const test = require('tap').test;
1010
const AdmZip = require('adm-zip');
1111
const ScratchStorage = require('scratch-storage').ScratchStorage;
1212
const VirtualMachine = require('../../src/index');
13+
const {sanitizeByteStream} = require('../../../scratch-svg-renderer/src/sanitize-svg');
1314

1415
const projectUri = path.resolve(__dirname, '../fixtures/offline-custom-assets.sb2');
1516
const projectZip = AdmZip(projectUri);
@@ -54,7 +55,7 @@ test('offline-custom-assets', t => {
5455

5556
const storedCostume = customCostume.asset;
5657
t.type(storedCostume, 'object');
57-
t.deepEquals(storedCostume.data, costumeData);
58+
t.same(storedCostume.data, sanitizeByteStream(costumeData));
5859

5960
const sounds = vm.runtime.targets[1].sprite.sounds;
6061
t.equals(sounds.length, 1);

packages/scratch-vm/test/integration/sb2_corrupted_svg.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const FakeBitmapAdapter = require('../fixtures/fake-bitmap-adapter');
1515
const {extractAsset, readFileToBuffer} = require('../fixtures/readProjectFile');
1616
const VirtualMachine = require('../../src/index');
1717
const {serializeCostumes} = require('../../src/serialization/serialize-assets');
18+
const {sanitizeByteStream} = require('../../../scratch-svg-renderer/src/sanitize-svg');
1819

1920
const projectUri = path.resolve(__dirname, '../fixtures/corrupt_svg.sb2');
2021
const project = readFileToBuffer(projectUri);
@@ -23,7 +24,7 @@ const originalCostume = extractAsset(projectUri, costumeFileName);
2324
// We need to get the actual md5 because we hand modified the svg to corrupt it
2425
// after we downloaded the project from Scratch
2526
// Loading the project back into the VM will correct the assetId and md5
26-
const brokenCostumeMd5 = md5(originalCostume);
27+
const brokenCostumeMd5 = md5(sanitizeByteStream(originalCostume));
2728

2829
global.Image = function () {
2930
const image = {
@@ -57,7 +58,7 @@ tap.beforeEach(() => {
5758
// Mock renderer breaking on loading a corrupt costume
5859
FakeRenderer.prototype.createSVGSkin = function (svgString) {
5960
// Look for text added to costume to make it a corrupt svg
60-
if (svgString.includes('<here is some')) {
61+
if (svgString.includes('<rect width="100 height=100 fill=">')) {
6162
throw new Error('mock createSVGSkin broke');
6263
}
6364
return FakeRenderer._nextSkinId++;

packages/scratch-vm/test/integration/sb3_corrupted_svg.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const FakeRenderer = require('../fixtures/fake-renderer');
1414
const {extractAsset, readFileToBuffer} = require('../fixtures/readProjectFile');
1515
const VirtualMachine = require('../../src/index');
1616
const {serializeCostumes} = require('../../src/serialization/serialize-assets');
17+
const {sanitizeByteStream} = require('../../../scratch-svg-renderer/src/sanitize-svg');
1718

1819
const projectUri = path.resolve(__dirname, '../fixtures/corrupt_svg.sb3');
1920
const project = readFileToBuffer(projectUri);
@@ -22,7 +23,7 @@ const originalCostume = extractAsset(projectUri, costumeFileName);
2223
// We need to get the actual md5 because we hand modified the svg to corrupt it
2324
// after we downloaded the project from Scratch
2425
// Loading the project back into the VM will correct the assetId and md5
25-
const brokenCostumeMd5 = md5(originalCostume);
26+
const brokenCostumeMd5 = md5(sanitizeByteStream(originalCostume));
2627

2728
let vm;
2829
let defaultVectorAssetId;
@@ -37,7 +38,7 @@ tap.beforeEach(() => {
3738
// Mock renderer breaking on loading a corrupt costume
3839
FakeRenderer.prototype.createSVGSkin = function (svgString) {
3940
// Look for text added to costume to make it a corrupt svg
40-
if (svgString.includes('<here is some')) {
41+
if (svgString.includes('<rect width="100 height=100 fill=">')) {
4142
throw new Error('mock createSVGSkin broke');
4243
}
4344
return FakeRenderer._nextSkinId++;

packages/scratch-vm/test/integration/sprite2_corrupted_svg.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const FakeBitmapAdapter = require('../fixtures/fake-bitmap-adapter');
1616
const {extractAsset, readFileToBuffer} = require('../fixtures/readProjectFile');
1717
const VirtualMachine = require('../../src/index');
1818
const {serializeCostumes} = require('../../src/serialization/serialize-assets');
19+
const {sanitizeByteStream} = require('../../../scratch-svg-renderer/src/sanitize-svg');
1920

2021
const projectUri = path.resolve(__dirname, '../fixtures/default.sb3');
2122
const project = readFileToBuffer(projectUri);
@@ -28,7 +29,7 @@ const originalCostume = extractAsset(spriteUri, costumeFileName);
2829
// We need to get the actual md5 because we hand modified the svg to corrupt it
2930
// after we downloaded the project from Scratch
3031
// Loading the project back into the VM will correct the assetId and md5
31-
const brokenCostumeMd5 = md5(originalCostume);
32+
const brokenCostumeMd5 = md5(sanitizeByteStream(originalCostume));
3233

3334
global.Image = function () {
3435
const image = {
@@ -61,7 +62,7 @@ tap.beforeEach(() => {
6162
// Mock renderer breaking on loading a corrupt costume
6263
FakeRenderer.prototype.createSVGSkin = function (svgString) {
6364
// Look for text added to costume to make it a corrupt svg
64-
if (svgString.includes('<here is some')) {
65+
if (svgString.includes('<rect width="100 height=100 fill=">')) {
6566
throw new Error('mock createSVGSkin broke');
6667
}
6768
return FakeRenderer.prototype._nextSkinId++;

packages/scratch-vm/test/integration/sprite3_corrupted_svg.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const FakeRenderer = require('../fixtures/fake-renderer');
1515
const {extractAsset, readFileToBuffer} = require('../fixtures/readProjectFile');
1616
const VirtualMachine = require('../../src/index');
1717
const {serializeCostumes} = require('../../src/serialization/serialize-assets');
18+
const {sanitizeByteStream} = require('../../../scratch-svg-renderer/src/sanitize-svg');
1819

1920
const projectUri = path.resolve(__dirname, '../fixtures/default.sb3');
2021
const project = readFileToBuffer(projectUri);
@@ -27,7 +28,7 @@ const originalCostume = extractAsset(spriteUri, costumeFileName);
2728
// We need to get the actual md5 because we hand modified the svg to corrupt it
2829
// after we downloaded the project from Scratch
2930
// Loading the project back into the VM will correct the assetId and md5
30-
const brokenCostumeMd5 = md5(originalCostume);
31+
const brokenCostumeMd5 = md5(sanitizeByteStream(originalCostume));
3132

3233
let vm;
3334
let defaultVectorAssetId;
@@ -42,7 +43,7 @@ tap.beforeEach(() => {
4243
// Mock renderer breaking on loading a corrupt costume
4344
FakeRenderer.prototype.createSVGSkin = function (svgString) {
4445
// Look for text added to costume to make it a corrupt svg
45-
if (svgString.includes('<here is some')) {
46+
if (svgString.includes('<rect width="100 height=100 fill=">')) {
4647
throw new Error('mock createSVGSkin broke');
4748
}
4849
return FakeRenderer.prototype._nextSkinId++;

0 commit comments

Comments
 (0)