Skip to content

Commit 69f378c

Browse files
package updates / npm vulnerability clean up (microsoft#10927)
* pass 1 * axios, request * gulp, cssnano, mocha * bump dompurify * gulp5 breaking change, need to explicitly output after merge, as streams close immediately * sanitize html * pin terser ver to clear uglifyify vuln * clear smoothie vuln * remove gulp-header; lodash vuln tagged in way that makes it unpinnable, and implementation is small * sanitize-html seems useless when we have dompurify and use it elsewhere for same thing * update marked (pxtlib/webapp portions mostly from codex, review) * fix error on pxt serve * attempt to fix gulp travis? * cull remaining uglify, fix test failure * Clean up multiple tags as well Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * remove legacy az media support * fix spacing typo * update smoothie.js * add back in sanitize-html * fix code blocks * restore summary parsing * docs fixed but need to clean up * marked -> 17 * partially back to clean, next to figure out docsrender w/o restoreCodeLanguageClassesFromTokens * fix cli server issue without mangling webapp logic * clean up other attempts at fixes --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent 9ba69f0 commit 69f378c

21 files changed

Lines changed: 1745 additions & 1379 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,13 @@ Currently, the following languages are included:
135135
* JavaScript
136136
* HTML,XML
137137
* Markdown
138+
* JSON
138139

139140
If you need to add other languages or update existing ones,
140141
you can find the distribution at [https://highlightjs.org/download/](https://highlightjs.org/download/);
141142
select all the languages you want to include (including the ones above!),
142143
download and unzip,
143-
and finally copy over `highlight.pack.js` into `webapp/public/highlight.js/`.
144+
and finally copy over `highlight.min.js` as `webapp/public/highlight.js/highlight.pack.js`.
144145

145146
## Tests
146147

‎cli/cli.ts‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,30 @@ import { SUB_WEBAPPS } from './subwebapp';
3434

3535
const rimraf: (f: string, opts: any, cb: (err: Error, res: any) => void) => void = require('rimraf');
3636

37-
pxt.docs.requireDOMSanitizer = () => require("sanitize-html");
37+
// dompurify requires a DOM implementation; sanitize-html does not.
38+
// sanitize-html is a bit more aggressive and culls class names by default from code snippets
39+
// Need to wrap to prevent that. Possibly worth swapping to jsdom + dompurify later for consistency.
40+
pxt.docs.requireDOMSanitizer = () => {
41+
const sanitizeHtml = require("sanitize-html");
42+
const defaults = sanitizeHtml.defaults || {};
43+
const baseAllowedAttrs = defaults.allowedAttributes || {};
44+
45+
const mergeClassAttribute = (tag: string) => {
46+
const existing: string[] = baseAllowedAttrs[tag] || [];
47+
return Array.from(new Set<string>([...existing, "class"]));
48+
};
49+
50+
const options = {
51+
...defaults,
52+
allowedAttributes: {
53+
...baseAllowedAttrs,
54+
code: mergeClassAttribute("code"),
55+
pre: mergeClassAttribute("pre"),
56+
},
57+
};
58+
59+
return (html: string) => sanitizeHtml(html, options);
60+
};
3861

3962
let forceCloudBuild = process.env["KS_FORCE_CLOUD"] !== "no";
4063
let forceLocalBuild = !!process.env["PXT_FORCE_LOCAL"];

‎cli/nodeutil.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export function readResAsync(g: events.EventEmitter) {
5757

5858
g.on("error", (err: any) => reject(err))
5959

60-
g.on('end', () => resolve(Buffer.concat(bufs)))
60+
g.on('end', () => resolve(Buffer.concat(bufs as unknown as Uint8Array[])))
6161
})
6262
}
6363

@@ -92,7 +92,7 @@ export function spawnWithPipeAsync(opts: SpawnOptions) {
9292
ch.on('close', (code: number) => {
9393
if (code != 0 && !opts.allowNonZeroExit)
9494
reject(new Error("Exit code: " + code + " from " + info))
95-
resolve(Buffer.concat(bufs))
95+
resolve(Buffer.concat(bufs as unknown as Uint8Array[]))
9696
});
9797
if (opts.input != null)
9898
ch.stdin.end(opts.input, "utf8")

‎cli/server.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,8 @@ export function expandHtml(html: string, params?: pxt.Map<string>, appTheme?: px
433433
// so we don't do them here either.
434434
}
435435
pxt.docs.prepTemplate(d)
436-
return d.finish().replace(/@-(\w+)-@/g, (f, w) => "@" + w + "@")
436+
const expanded = d.finish().replace(/@-(\w+)-@/g, (f, w) => "@" + w + "@")
437+
return expanded
437438
}
438439

439440
export function expandDocTemplateCore(template: string) {

‎gulpfile.js‎

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
const fs = require("fs");
22
const os = require("os");
33
const path = require("path");
4+
const { Transform, PassThrough } = require("stream");
5+
const { minify } = require("terser");
46

57
const gulp = require("gulp");
68
const ts = require("gulp-typescript");
79
const merge = require("merge-stream");
810
const concat = require("gulp-concat");
9-
const header = require("gulp-header");
1011
const replace = require("gulp-replace");
1112
const ju = require("./jakeutil");
1213
const yargs = require('yargs');
@@ -21,6 +22,63 @@ const clean = () => rimraf("built").then(() => rimraf("temp"));
2122
const update = () => exec("git pull", true).then(() => exec("npm install", true))
2223
const noop = () => Promise.resolve();
2324

25+
function prependHeader(headerText) {
26+
const headerBuffer = Buffer.from(headerText);
27+
28+
return new Transform({
29+
objectMode: true,
30+
transform(file, _enc, callback) {
31+
if (file.isNull()) {
32+
return callback(null, file);
33+
}
34+
35+
if (file.isBuffer && file.isBuffer()) {
36+
file.contents = Buffer.concat([headerBuffer, file.contents]);
37+
return callback(null, file);
38+
}
39+
40+
if (file.isStream && file.isStream()) {
41+
const combined = new PassThrough();
42+
combined.write(headerBuffer);
43+
file.contents.on('error', err => combined.emit('error', err));
44+
file.contents.pipe(combined, { end: true });
45+
file.contents = combined;
46+
return callback(null, file);
47+
}
48+
49+
return callback(null, file);
50+
}
51+
});
52+
}
53+
54+
function terserMinifyTransform(options = {}) {
55+
return new Transform({
56+
objectMode: true,
57+
transform(file, _enc, callback) {
58+
if (file.isNull()) {
59+
return callback(null, file);
60+
}
61+
62+
if (file.isStream && file.isStream()) {
63+
return callback(new Error("Streaming not supported for terserMinifyTransform"));
64+
}
65+
66+
const source = file.contents.toString("utf8");
67+
68+
minify(source, options)
69+
.then(result => {
70+
if (!result || typeof result.code !== "string") {
71+
throw new Error("Terser produced no output");
72+
}
73+
74+
file.contents = Buffer.from(result.code, "utf8");
75+
callback(null, file);
76+
})
77+
.catch(err => callback(err));
78+
}
79+
});
80+
}
81+
2482
const SUB_WEBAPPS = require("./cli/webapps-config.json").webapps;
2583

2684
/** onlineline */
@@ -71,7 +129,7 @@ const pxtworker = () => gulp.src([
71129
"built/pxtpy.js"
72130
])
73131
.pipe(concat("pxtworker.js"))
74-
.pipe(header(`"use strict";\n`))
132+
.pipe(prependHeader(`"use strict";\n`))
75133
.pipe(gulp.dest("built/web"));
76134

77135
const pxtembed = () => gulp.src([
@@ -96,7 +154,7 @@ const buildpxtjs = () => gulp.src([
96154
"built/cli.js"
97155
])
98156
.pipe(concat("pxt.js"))
99-
.pipe(header(`
157+
.pipe(prependHeader(`
100158
"use strict";
101159
// make sure TypeScript doesn't overwrite our module.exports
102160
global.savedModuleExports = module.exports;
@@ -137,7 +195,7 @@ function initWatch() {
137195
webapp,
138196
browserifyWebapp,
139197
browserifyAssetEditor,
140-
gulp.parallel(semanticjs, copyJquery, copyWebapp, copySemanticFonts, copyMonaco),
198+
gulp.parallel(semanticjs, copyJquery, copyMarked, copySmoothie, copyWebapp, copySemanticFonts, copyMonaco),
141199
notifyBuildComplete
142200
];
143201

@@ -203,10 +261,23 @@ function compileTsProject(dirname, destination, useOutdir, filename) {
203261
let tsResult = tsProject.src()
204262
.pipe(tsProject());
205263

206-
return merge(
207-
tsResult.js.pipe(gulp.dest(destination)),
208-
tsResult.dts.pipe(gulp.dest(destination))
209-
);
264+
const outputStreams = [];
265+
266+
if (tsResult.js) {
267+
outputStreams.push(tsResult.js);
268+
}
269+
270+
if (tsResult.dts) {
271+
outputStreams.push(tsResult.dts);
272+
}
273+
274+
const mergedOutput = outputStreams.length > 1 ? merge(...outputStreams) : outputStreams[0];
275+
276+
if (!mergedOutput) {
277+
return Promise.resolve();
278+
}
279+
280+
return mergedOutput.pipe(gulp.dest(destination));
210281
}
211282

212283
// TODO: Copied from Jakefile; should be async
@@ -381,6 +452,18 @@ const copyJquery = () => gulp.src("node_modules/jquery/dist/jquery.min.js")
381452
.pipe(concat("jquery.js"))
382453
.pipe(gulp.dest("built/web"));
383454

455+
const copyMarked = () =>
456+
gulp.src("node_modules/marked/lib/marked.umd.js")
457+
.pipe(concat("marked.min.js"))
458+
.pipe(gulp.dest("built/web/marked"))
459+
.pipe(gulp.dest("webapp/public/marked"));
460+
461+
const copySmoothie = () =>
462+
gulp.src("node_modules/smoothie/smoothie.js")
463+
.pipe(terserMinifyTransform())
464+
.pipe(concat("smoothie_compressed.js"))
465+
.pipe(gulp.dest("webapp/public/smoothie"));
466+
384467
const copyWebapp = () =>
385468
gulp.src([
386469
"node_modules/@microsoft/applicationinsights-web/browser/ai.2.min.js",
@@ -399,8 +482,10 @@ const copyWebapp = () =>
399482
const copySemanticFonts = () => gulp.src("node_modules/semantic-ui-less/themes/default/assets/fonts/*")
400483
.pipe(gulp.dest("built/web/fonts"))
401484

485+
// NOTE: Uglifyify no longer runs here because it crashes on modern syntax emitted in
486+
// production builds; minification is handled later in runUglify via Terser.
402487
const execBrowserify = (entryPoint, outfile) => process.env.PXT_ENV == 'production' ?
403-
exec(`node node_modules/browserify/bin/cmd ${entryPoint} -g [ envify --NODE_ENV production ] -g [ uglifyify --ignore '**/node_modules/@blockly/**' ] -o ${outfile}`) :
488+
exec(`node node_modules/browserify/bin/cmd ${entryPoint} -g [ envify --NODE_ENV production ] -o ${outfile}`) :
404489
exec(`node node_modules/browserify/bin/cmd ${entryPoint} -o ${outfile} --debug`);
405490

406491
const browserifyWebapp = () => execBrowserify("./built/webapp/src/app.js", "./built/web/main.js");
@@ -719,11 +804,11 @@ function testTask(testFolder, testFile, additionalFiles) {
719804

720805
const buildTestRunner = () => gulp.src(src)
721806
.pipe(concat("runner.js"))
722-
.pipe(header(`
807+
.pipe(prependHeader(`
723808
"use strict";
724809
// make sure TypeScript doesn't overwrite our module.exports
725810
global.savedModuleExports = module.exports;
726-
module.exports = null;
811+
module.exports = {};
727812
`))
728813
.pipe(gulp.dest('built/tests/' + testFolder));
729814

@@ -766,7 +851,7 @@ const buildAll = gulp.series(
766851
webapp,
767852
browserifyWebapp,
768853
browserifyAssetEditor,
769-
gulp.parallel(semanticjs, copyJquery, copyWebapp, copySemanticFonts, copyMonaco),
854+
gulp.parallel(semanticjs, copyJquery, copyMarked, copySmoothie, copyWebapp, copySemanticFonts, copyMonaco),
770855
buildBlocksTestRunner,
771856
gulp.parallel(browserifyBlocksTestRunner, browserifyBlocksPrep),
772857
runUglify

‎package.json‎

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,14 @@
4949
"tests/blocks-test/karma.conf.js",
5050
"built/tests/blocksrunner.js",
5151
"built/tests/blockssetup.js",
52-
"scripts/patchUglifyify.js",
5352
"docfiles",
5453
"theme",
5554
"common-docs",
5655
"react-common"
5756
],
5857
"main": "built/pxt.js",
5958
"engines": {
60-
"node": ">=16.0.0",
59+
"node": ">=18.0.0",
6160
"npm": ">=8.0.0"
6261
},
6362
"dependencies": {
@@ -71,34 +70,33 @@
7170
"@types/diff-match-patch": "^1.0.32",
7271
"@zip.js/zip.js": "2.4.20",
7372
"adm-zip": "^0.5.12",
74-
"axios": "^1.6.8",
73+
"axios": "^1.12.2",
7574
"blockly": "12.2.0",
7675
"browserify": "17.0.0",
7776
"chai": "^3.5.0",
7877
"chalk": "^4.1.2",
79-
"cssnano": "4.1.10",
78+
"cssnano": "7.1.1",
8079
"dashjs": "^4.4.0",
8180
"diff-match-patch": "^1.0.5",
82-
"dompurify": "2.0.17",
81+
"dompurify": "3.3.0",
8382
"faye-websocket": "0.11.1",
8483
"karma": "6.4.4",
8584
"karma-chai": "0.1.0",
8685
"karma-chrome-launcher": "3.2.0",
8786
"karma-mocha": "2.0.1",
8887
"less": "3.13.1",
8988
"lzma": "2.3.2",
90-
"marked": "0.3.19",
91-
"mocha": "8.4.0",
89+
"marked": "17.0.0",
90+
"mocha": "10.8.2",
9291
"pngjs": "3.4.0",
93-
"postcss": "6.0.21",
92+
"postcss": "8.5.6",
9493
"promise.prototype.finally": "^3.1.2",
9594
"puppeteer": "23.11.1",
96-
"request": "2.88.0",
9795
"rimraf": "2.5.4",
98-
"rtlcss": "2.2.1",
99-
"sanitize-html": "2.3.2",
96+
"rtlcss": "4.3.0",
97+
"sanitize-html": "2.17.0",
10098
"semantic-ui-less": "2.4.1",
101-
"terser": "5.6.0"
99+
"terser": "5.44.0"
102100
},
103101
"lazyDependencies": {
104102
"node-hid": "^0.7.2",
@@ -111,15 +109,13 @@
111109
"@types/fuse": "2.5.2",
112110
"@types/highlight.js": "9.12.2",
113111
"@types/jquery": "3.3.29",
114-
"@types/marked": "0.3.0",
115-
"@types/mocha": "^2.2.44",
112+
"@types/mocha": "^10.0.10",
116113
"@types/node": "10.14.2",
117114
"@types/react": "17.0.50",
118115
"@types/react-dom": "17.0.17",
119116
"@types/react-modal": "3.13.1",
120117
"@types/react-redux": "7.1.24",
121118
"@types/react-tooltip": "3.3.6",
122-
"@types/request": "2.48.5",
123119
"@types/resize-observer-browser": "^0.1.5",
124120
"@types/web-bluetooth": "0.0.4",
125121
"@typescript-eslint/eslint-plugin": "^4.20.0",
@@ -132,32 +128,37 @@
132128
"eslint-plugin-jsx-a11y": "^6.4.1",
133129
"eslint-plugin-react": "^7.23.1",
134130
"fuse.js": "3.2.0",
135-
"gulp": "4.0.0",
131+
"gulp": "5.0.1",
132+
"gulp-cli": "3.1.0",
136133
"gulp-concat": "2.6.1",
137-
"gulp-header": "2.0.9",
138-
"gulp-replace": "1.0.0",
134+
"gulp-replace": "1.1.4",
139135
"gulp-typescript": "5.0.1",
140-
"highlight.js": "9.12.0",
136+
"highlight.js": "11.11.1",
141137
"jquery": "3.5.0",
142138
"merge-stream": "2.0.0",
143139
"monaco-editor": "0.24.0",
144-
"pouchdb": "7.2.1",
145-
"pouchdb-adapter-memory": "7.2.1",
140+
"pouchdb": "7.3.1",
141+
"pouchdb-adapter-memory": "7.3.1",
146142
"react": "17.0.2",
147143
"react-dom": "17.0.2",
148144
"react-modal": "3.15.1",
149145
"react-redux": "7.2.9",
150146
"react-tooltip": "3.9.0",
151147
"redux": "4.2.0",
152-
"smoothie": "1.35.0",
148+
"smoothie": "1.36.1",
153149
"typescript": "4.8.3",
154-
"uglifyify": "5.0.2",
155150
"yargs": "^17.7.2"
156151
},
157152
"overrides": {
158153
"combine-source-map": {
159154
"source-map": "0.4.4"
160-
}
155+
},
156+
"sha.js": "2.4.12",
157+
"cipher-base": "1.0.7",
158+
"pbkdf2": "3.1.5",
159+
"elliptic": "6.6.1",
160+
"tar-fs": "3.1.1",
161+
"terser": "5.44.0"
161162
},
162163
"scripts": {
163164
"build": "gulp",
@@ -175,7 +176,6 @@
175176
"test:tutorials": "gulp testtutorials",
176177
"update": "gulp update",
177178
"watch-streamer": "cd docs/static/streamer && tsc -t es6 --watch",
178-
"prepare": "node ./scripts/npm-prepare.js",
179-
"postinstall": "node ./scripts/patchUglifyify.js"
179+
"prepare": "node ./scripts/npm-prepare.js"
180180
}
181181
}

0 commit comments

Comments
 (0)