From 0991a21f40074cc902930ce63830ba1a83786c46 Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 21 Jul 2026 01:07:18 +0200 Subject: [PATCH] Fix prototype-named code modules --- spec/engine/game/modulesSpec.js | 69 +++++++++++++++++++++++++++++++++ src/game/game.js | 10 +++-- 2 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 spec/engine/game/modulesSpec.js diff --git a/spec/engine/game/modulesSpec.js b/spec/engine/game/modulesSpec.js new file mode 100644 index 00000000..bc0a0c5c --- /dev/null +++ b/spec/engine/game/modulesSpec.js @@ -0,0 +1,69 @@ +const utils = require('../../../src/utils'), + driver = utils.getDriver(), + game = require('../../../src/game/game'); + +describe('Code modules', () => { + let originalEvalCode; + + beforeEach(() => { + originalEvalCode = driver.evalCode; + driver.evalCode = (module, globals) => { + const factory = new Function('module', 'exports', 'require', 'global', module.code); + factory(module, module.exports, globals.require, globals); + }; + }); + + afterEach(() => { + driver.evalCode = originalEvalCode; + }); + + function runModule(moduleName, includeModule = true) { + const userId = `module-test-${moduleName}-${includeModule}`; + const globals = {}; + const runtimeData = { + user: {_id: userId, gcl: 0, power: 0, cpu: 20, resources: {}}, + userObjects: {}, + userPowerCreeps: {}, + roomObjects: {}, + rooms: {}, + flags: [], + time: 1, + cpu: 20, + cpuBucket: 10000, + accessibleRooms: '[]', + staticTerrainData: {W0N0: new Uint8Array(2500)}, + roomStatusData: {}, + mapGrid: {gridData: {}}, + transactions: {incoming: [], outgoing: []}, + market: {orders: {}, history: {}}, + userCodeTimestamp: 1 + }; + const codeModules = JSON.parse(JSON.stringify({ + main: `module.exports.loop = () => { global.result = require(${JSON.stringify(moduleName)}); }` + })); + if(includeModule) { + Object.defineProperty(codeModules, moduleName, { + value: 'module.exports = 42', + enumerable: true + }); + } + + game.init( + globals, codeModules, runtimeData, {push() {}}, {get() { return '{}'; }}, + {log() {}, commandResult() {}}, [], 100, () => 0, undefined, fn => fn + ); + game.run(userId); + + return globals.result; + } + + ['constructor', 'toString', '__proto__'].forEach(moduleName => { + it(`loads a module named ${moduleName}`, () => { + expect(runModule(moduleName)).toBe(42); + }); + }); + + it('rejects an unknown module whose name is inherited from Object.prototype', () => { + expect(() => runModule('constructor', false)).toThrowError("Unknown module 'constructor'"); + }); +}); diff --git a/src/game/game.js b/src/game/game.js index 856168f7..d6a1c0a6 100644 --- a/src/game/game.js +++ b/src/game/game.js @@ -8,6 +8,7 @@ market = require('./market'), customPrototypes = require('./custom-prototypes'), bindFunction = Function.call.bind(Function.bind), + hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty), objectCreate = Object.create, jsonStringify = JSON.stringify, jsonParse = JSON.parse; @@ -507,7 +508,8 @@ runCodeCache[userId].globals.require.cache.main.loop)) { runCodeCache[userId].globals.require = bindFunction(requireFn, runCodeCache[userId]); - runCodeCache[userId].globals.require.cache = {lodash: runCodeCache[userId].globals._}; + runCodeCache[userId].globals.require.cache = objectCreate(null); + runCodeCache[userId].globals.require.cache.lodash = runCodeCache[userId].globals._; runCodeCache[userId].globals.require.timestamp = runCodeCache[userId].runtimeData.userCodeTimestamp; } @@ -552,9 +554,9 @@ moduleName = moduleName.replace(/^\.\//,''); - if (!(moduleName in this.globals.require.cache)) { + if (!hasOwnProperty(this.globals.require.cache, moduleName)) { - if (_.isUndefined(this.codeModules[moduleName])) { + if (!hasOwnProperty(this.codeModules, moduleName) || _.isUndefined(this.codeModules[moduleName])) { throw new Error(`Unknown module '${moduleName}'`); } @@ -582,7 +584,7 @@ this.globals.require.cache[moduleName] = moduleObject.exports; if (moduleObject.__initGlobals) { - this.globals.require.initGlobals = this.globals.require.initGlobals || {}; + this.globals.require.initGlobals = this.globals.require.initGlobals || objectCreate(null); this.globals.require.initGlobals[moduleName] = moduleObject.__initGlobals; } }