diff --git a/packages/cli-repl/src/cli-repl.spec.ts b/packages/cli-repl/src/cli-repl.spec.ts index 93ddb715c9..f2f9ff506f 100644 --- a/packages/cli-repl/src/cli-repl.spec.ts +++ b/packages/cli-repl/src/cli-repl.spec.ts @@ -161,6 +161,18 @@ describe('CliRepl', () => { ]); }); + it('fails when trying to overwrite mongosh-owned config settings', async() => { + output = ''; + input.write('config.set("userId", "foo")\n'); + await waitEval(cliRepl.bus); + expect(output).to.include('Option "userId" is not available in this environment'); + + output = ''; + input.write('config.get("userId")\n'); + await waitEval(cliRepl.bus); + expect(output).to.match(/^[a-z0-9]{24}\n> $/); + }); + context('loading JS files from disk', () => { it('allows loading a file from the disk', async() => { const filenameA = path.resolve(__dirname, '..', 'test', 'fixtures', 'load', 'a.js'); diff --git a/packages/cli-repl/src/mongosh-repl.spec.ts b/packages/cli-repl/src/mongosh-repl.spec.ts index 1d2a38a883..0ef2854468 100644 --- a/packages/cli-repl/src/mongosh-repl.spec.ts +++ b/packages/cli-repl/src/mongosh-repl.spec.ts @@ -2,6 +2,7 @@ import { MongoshCommandFailed } from '@mongosh/errors'; import { bson, ServiceProvider } from '@mongosh/service-provider-core'; import { ADMIN_DB } from '@mongosh/shell-api/lib/enums'; +import { CliUserConfig } from '@mongosh/types'; import { EventEmitter, once } from 'events'; import path from 'path'; import { Duplex, PassThrough } from 'stream'; @@ -37,11 +38,12 @@ describe('MongoshNodeRepl', () => { outputStream.setEncoding('utf8').on('data', (chunk) => { output += chunk; }); bus = new EventEmitter(); - config = {}; + config = new CliUserConfig(); const cp = stubInterface(); cp.getHistoryFilePath.returns(path.join(tmpdir.path, 'history')); cp.getConfig.callsFake(async(key: string) => config[key]); cp.setConfig.callsFake(async(key: string, value: any) => { config[key] = value; return 'success'; }); + cp.listConfigOptions.callsFake(() => Object.keys(config)); cp.exit.callsFake(((code) => bus.emit('test-exit-event', code)) as any); ioProvider = cp; diff --git a/packages/node-runtime-worker-thread/src/worker-runtime.spec.ts b/packages/node-runtime-worker-thread/src/worker-runtime.spec.ts index 35c5943c4b..e9d57363d7 100644 --- a/packages/node-runtime-worker-thread/src/worker-runtime.spec.ts +++ b/packages/node-runtime-worker-thread/src/worker-runtime.spec.ts @@ -476,7 +476,7 @@ describe('worker', () => { }, getConfig() {}, setConfig() {}, - listConfigOptions() { return []; }, + listConfigOptions() { return ['batchSize']; }, onRunInterruptible() {} }; @@ -555,8 +555,8 @@ describe('worker', () => { await init('mongodb://nodb/', {}, { nodb: true }); - await evaluate('config.set("key", "value")'); - expect(evalListener.setConfig).to.have.been.calledWith('key', 'value'); + await evaluate('config.set("batchSize", 200)'); + expect(evalListener.setConfig).to.have.been.calledWith('batchSize', 200); }); }); diff --git a/packages/shell-api/src/shell-api.spec.ts b/packages/shell-api/src/shell-api.spec.ts index f8d64b1c65..d0c87e542f 100644 --- a/packages/shell-api/src/shell-api.spec.ts +++ b/packages/shell-api/src/shell-api.spec.ts @@ -622,9 +622,9 @@ describe('ShellApi', () => { beforeEach(() => { config = internalState.context.config; - store = {}; + store = { somekey: '' }; evaluationListener.setConfig.callsFake(async(key, value) => { - if (key === 'unavailable' as any) return 'ignored'; + if (key === 'ignoreme' as any) return 'ignored'; store[key] = value; return 'success'; }); @@ -647,6 +647,10 @@ describe('ShellApi', () => { it('rejects setting unavailable config keys', async() => { expect(await config.set('unavailable', 'value')).to.equal('Option "unavailable" is not available in this environment'); }); + + it('rejects setting explicitly ignored config keys', async() => { + expect(await config.set('ignoreme', 'value')).to.equal('Option "ignoreme" is not available in this environment'); + }); }); context('with a no-config evaluation listener', () => { diff --git a/packages/shell-api/src/shell-api.ts b/packages/shell-api/src/shell-api.ts index 4523e023db..e251074fec 100644 --- a/packages/shell-api/src/shell-api.ts +++ b/packages/shell-api/src/shell-api.ts @@ -42,7 +42,8 @@ class ShellConfig extends ShellApiClass { async set(key: K, value: ShellUserConfig[K]): Promise { assertArgsDefinedType([key], ['string'], 'config.set'); const { evaluationListener } = this._internalState; - const result = await evaluationListener.setConfig?.(key, value); + // Only allow known config keys here: + const result = (await this._allKeys()).includes(key) && await evaluationListener.setConfig?.(key, value); if (result !== 'success') { return `Option "${key}" is not available in this environment`; } @@ -57,12 +58,15 @@ class ShellConfig extends ShellApiClass { return await evaluationListener.getConfig?.(key) ?? this.defaults[key]; } - async [asPrintable](): Promise> { + async _allKeys(): Promise<(keyof ShellUserConfig)[]> { const { evaluationListener } = this._internalState; - const keys = (await evaluationListener.listConfigOptions?.() ?? Object.keys(this.defaults)) as (keyof ShellUserConfig)[]; + return (await evaluationListener.listConfigOptions?.() ?? Object.keys(this.defaults)) as (keyof ShellUserConfig)[]; + } + + async [asPrintable](): Promise> { return new Map( await Promise.all( - keys.map( + (await this._allKeys()).map( async key => [key, await this.get(key)] as const))); } }