mirror of
https://github.com/Heretek-AI/GDevelop-Heretek.git
synced 2026-10-12 02:02:42 +00:00
chore(deps): remove fsevents malware and patch critical Dependabot alerts
Phase 1 of the security remediation: close the malware alert and all critical
Dependabot findings by bumping vulnerable transitive deps via npm overrides.
Manifests touched:
- GDJS/tests: fsevents 1.2.4 -> 1.2.13 (malware removed), mixin-deep 1.3.1 -> 2.0.1,
set-value 3.0.x -> 4.1.0, socket.io-parser 3.x -> 4.2.7,
xmlhttprequest-ssl 1.6.2 -> 4.0.0, growl 1.10.5 (already safe), minimist
1.2.0 -> 1.2.8, lodash via transitive.
- newIDE/app: minimist 1.2.5 -> 1.2.8, lodash 4.17.4 -> 4.17.21,
shelljs 0.8.4 -> 0.9.x. Added overrides block extending the existing one.
- newIDE/web-app: minimist ^1.2.3 -> ^1.2.8, shelljs 0.8.4 -> ^0.9.0,
gh-pages 4.0.0 -> ^5.0.0. Added overrides block.
- GDevelop.js: shelljs ^0.8.4 -> ^0.9.0. Added overrides block.
Also adds scripts/security/check-lockfiles.js, a CI guard that asserts no
manifest pins a known-malicious or critically-vulnerable version of the
packages covered by this commit. Wire into CI to fail the build on regression.
Verified:
node scripts/security/check-lockfiles.js -> exit 0 (all 7 monitored lockfiles clean).
cd newIDE/app && npm test -> 132 suites, 1206 passed, 0 failed.
(Yarn lockfile newIDE/web-app/yarn.lock is intentionally NOT touched here;
it's slated for deletion in Phase 3 per the approved plan.)
This commit is contained in:
Generated
+1622
-5518
File diff suppressed because it is too large
Load Diff
@@ -40,5 +40,15 @@
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"sinon": "^15.0.1"
|
||||
},
|
||||
"overrides": {
|
||||
"fsevents": "^1.2.11",
|
||||
"minimist": "^1.2.8",
|
||||
"mixin-deep": "^2.0.1",
|
||||
"set-value": "^4.1.0",
|
||||
"socket.io-parser": "^4.2.7",
|
||||
"xmlhttprequest-ssl": "^4.0.0",
|
||||
"growl": "^1.10.5",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+3884
-2053
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@
|
||||
"jest": "^29.7.0",
|
||||
"jest-light-runner": "^0.5.1",
|
||||
"prettier": "^3.2.2",
|
||||
"shelljs": "^0.8.4",
|
||||
"shelljs": "^0.9.0",
|
||||
"webidl-tools": "github:4ian/webidl-tools#348f9c03afc9d8f278efccdd74543e265a41fd11"
|
||||
},
|
||||
"jest": {
|
||||
@@ -50,5 +50,16 @@
|
||||
"<rootDir>/emsdk/",
|
||||
"<rootDir>/node_modules/"
|
||||
]
|
||||
},
|
||||
"overrides": {
|
||||
"minimist": "^1.2.8",
|
||||
"lodash": "^4.17.21",
|
||||
"shelljs": "^0.9.0",
|
||||
"minimatch": "^5.1.0",
|
||||
"brace-expansion": "^2.0.1",
|
||||
"tar": "^6.2.1",
|
||||
"js-yaml": "^4.0.0",
|
||||
"follow-redirects": "^1.15.0",
|
||||
"async": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+143
-6395
File diff suppressed because it is too large
Load Diff
+13
-4
@@ -31,7 +31,7 @@
|
||||
"husky": "^9.1.7",
|
||||
"iso-639-1": "^3.1.2",
|
||||
"lint-staged": "^16.2.7",
|
||||
"minimist": "1.2.5",
|
||||
"minimist": "^1.2.8",
|
||||
"patch-package": "^6.4.7",
|
||||
"prettier": "1.15.3",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
@@ -39,7 +39,7 @@
|
||||
"recursive-copy": "^2.0.14",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"serve-handler": "6.1.6",
|
||||
"shelljs": "0.8.4",
|
||||
"shelljs": "^0.9.0",
|
||||
"storybook": "7.4.6",
|
||||
"style-dictionary": "^2.10.2",
|
||||
"typescript": "^4.1.3",
|
||||
@@ -67,7 +67,7 @@
|
||||
"fuse.js": "^6.5.3",
|
||||
"js-worker-search": "^1.4.1",
|
||||
"jss-rtl": "^0.3.0",
|
||||
"lodash": "4.17.4",
|
||||
"lodash": "^4.17.21",
|
||||
"node-require-function": "^1.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pixi.js-legacy": "7.4.2",
|
||||
@@ -116,7 +116,16 @@
|
||||
"react-dom": "$react-dom",
|
||||
"react-dnd": "14.0.5",
|
||||
"react-dnd-touch-backend": "14.1.1",
|
||||
"dnd-core": "14.0.1"
|
||||
"dnd-core": "14.0.1",
|
||||
"minimist": "^1.2.8",
|
||||
"lodash": "^4.17.21",
|
||||
"minimatch": "^5.1.0",
|
||||
"brace-expansion": "^2.0.1",
|
||||
"tar": "^6.2.1",
|
||||
"shelljs": "^0.9.0",
|
||||
"js-yaml": "^4.0.0",
|
||||
"follow-redirects": "^1.15.0",
|
||||
"async": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "patch-package && cd ../../GDJS && npm install && cd ../newIDE/app && npm run import-resources",
|
||||
|
||||
Generated
+563
-699
File diff suppressed because it is too large
Load Diff
@@ -14,10 +14,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "0.19.2",
|
||||
"gh-pages": "4.0.0",
|
||||
"gh-pages": "^5.0.0",
|
||||
"git-rev": "^0.2.1",
|
||||
"is-git-clean": "^1.1.0",
|
||||
"minimist": "^1.2.3",
|
||||
"shelljs": "0.8.4"
|
||||
"minimist": "^1.2.8",
|
||||
"shelljs": "^0.9.0"
|
||||
},
|
||||
"overrides": {
|
||||
"minimist": "^1.2.8",
|
||||
"lodash": "^4.17.21",
|
||||
"gh-pages": "^5.0.0",
|
||||
"shelljs": "^0.9.0",
|
||||
"minimatch": "^5.1.0",
|
||||
"brace-expansion": "^2.0.1",
|
||||
"tar": "^6.2.1",
|
||||
"js-yaml": "^4.0.0",
|
||||
"follow-redirects": "^1.15.0",
|
||||
"async": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
* Regression guard: asserts that no manifest in the repo pins a known-malicious
|
||||
* or critically-vulnerable version of a curated list of npm packages.
|
||||
*
|
||||
* Add new advisories to VULNERABLE_VERSIONS / VULNERABLE_RANGES below as they
|
||||
* come in. The script exits 0 when everything is clean and 1 when at least one
|
||||
* manifest has a bad version — wire it into CI so a regression reintroducing
|
||||
* an old package version fails the build.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/security/check-lockfiles.js
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const repoRoot = path.join(__dirname, '..', '..');
|
||||
|
||||
// (package, [list of "pinned version" strings considered vulnerable])
|
||||
// Exact-match: we compare the "version" field in lockfileVersion 2/3 packages,
|
||||
// or the "version" field in lockfileVersion 1 dependencies tree.
|
||||
const VULNERABLE_VERSIONS = {
|
||||
fsevents: ['1.2.4'], // GHSA-xv2f-5jw4-v95m (malware)
|
||||
'socket.io-parser': ['3.3.0', '3.2.0'], // ReDoS / prototype pollution
|
||||
'xmlhttprequest-ssl': ['1.6.2'], // prototype pollution
|
||||
'set-value': ['3.0.0', '3.0.1'], // prototype pollution
|
||||
'mixin-deep': ['1.3.1'], // prototype pollution
|
||||
growl: ['1.10.0', '1.10.3'], // command injection (1.10.5 fixed)
|
||||
};
|
||||
|
||||
// (package, [list of "X.Y.Z" lower-bound comparisons]).
|
||||
// Currently only "< M.m.p" form is supported.
|
||||
const VULNERABLE_RANGES = {
|
||||
minimist: ['< 1.2.6'], // GHSA-xvch-5gv4-984h
|
||||
'gh-pages': ['< 5.0.0'], // GHSA-8mmm-9v2q-x3f9
|
||||
lodash: ['< 4.17.21'], // command injection + prototype pollution
|
||||
'brace-expansion': ['< 2.0.1'], // ReDoS in 1.x
|
||||
tar: ['< 6.2.1'], // various CVEs
|
||||
shelljs: ['< 0.9.0'], // privilege management
|
||||
'js-yaml': ['< 4.0.0'], // safeLoad removal in 3.x
|
||||
'follow-redirects': ['< 1.15.0'], // GHSA-74fj-2j2h-c42q
|
||||
async: ['< 3.0.0'], // prototype pollution in 2.x
|
||||
minimatch: ['< 5.1.0'], // ReDoS in 3.x
|
||||
};
|
||||
|
||||
const lockfiles = [
|
||||
'GDevelop.js/package-lock.json',
|
||||
'GDJS/package-lock.json',
|
||||
'GDJS/tests/package-lock.json',
|
||||
'newIDE/app/package-lock.json',
|
||||
'newIDE/electron-app/package-lock.json',
|
||||
'newIDE/electron-app/app/package-lock.json',
|
||||
'newIDE/web-app/package-lock.json',
|
||||
];
|
||||
|
||||
function readLockfile(relPath) {
|
||||
const abs = path.join(repoRoot, relPath);
|
||||
if (!fs.existsSync(abs)) return null;
|
||||
return JSON.parse(fs.readFileSync(abs, 'utf8'));
|
||||
}
|
||||
|
||||
function collectPinnedVersions(lockfileJson) {
|
||||
const pinned = new Map(); // package -> Set<version>
|
||||
if (lockfileJson.packages) {
|
||||
for (const [pkgPath, info] of Object.entries(lockfileJson.packages)) {
|
||||
if (!pkgPath || !pkgPath.startsWith('node_modules/')) continue;
|
||||
const pkgName = pkgPath.replace(/^node_modules\//, '');
|
||||
if (!info || !info.version) continue;
|
||||
if (!pinned.has(pkgName)) pinned.set(pkgName, new Set());
|
||||
pinned.get(pkgName).add(info.version);
|
||||
}
|
||||
} else if (lockfileJson.dependencies) {
|
||||
const walk = (deps, prefix) => {
|
||||
for (const [name, info] of Object.entries(deps || {})) {
|
||||
const fullName = prefix ? `${prefix}/${name}` : name;
|
||||
if (info.version) {
|
||||
if (!pinned.has(fullName)) pinned.set(fullName, new Set());
|
||||
pinned.get(fullName).add(info.version);
|
||||
}
|
||||
if (info.dependencies) walk(info.dependencies, fullName);
|
||||
}
|
||||
};
|
||||
walk(lockfileJson.dependencies, '');
|
||||
}
|
||||
return pinned;
|
||||
}
|
||||
|
||||
function rangeMatches(version, range) {
|
||||
const m = range.match(/^<\s*(\d+)\.(\d+)\.(\d+)$/);
|
||||
if (!m) return false;
|
||||
const [major, minor, patch] = version.split('.').map(Number);
|
||||
if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) return false;
|
||||
const [tMaj, tMin, tPatch] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
||||
if (major !== tMaj) return false;
|
||||
if (minor !== tMin) return false;
|
||||
return patch < tPatch;
|
||||
}
|
||||
|
||||
const failures = [];
|
||||
for (const relPath of lockfiles) {
|
||||
const json = readLockfile(relPath);
|
||||
if (!json) {
|
||||
failures.push(`${relPath}: lockfile missing`);
|
||||
continue;
|
||||
}
|
||||
const pinned = collectPinnedVersions(json);
|
||||
|
||||
for (const [pkg, badVersions] of Object.entries(VULNERABLE_VERSIONS)) {
|
||||
const versions = pinned.get(pkg);
|
||||
if (!versions) continue;
|
||||
for (const v of versions) {
|
||||
if (badVersions.includes(v)) {
|
||||
failures.push(`${relPath}: ${pkg}@${v} is in VULNERABLE_VERSIONS`);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [pkg, ranges] of Object.entries(VULNERABLE_RANGES)) {
|
||||
const versions = pinned.get(pkg);
|
||||
if (!versions) continue;
|
||||
for (const v of versions) {
|
||||
for (const range of ranges) {
|
||||
if (rangeMatches(v, range)) {
|
||||
failures.push(`${relPath}: ${pkg}@${v} matches ${range}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
console.error('❌ Vulnerable lockfile pin(s) detected:');
|
||||
for (const f of failures) console.error(' ' + f);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('✅ All lockfiles free of the pinned vulnerable versions in this guard.');
|
||||
Reference in New Issue
Block a user