VYPR
High severityNVD Advisory· Published Oct 6, 2023· Updated Sep 19, 2024

CVE-2023-45282

CVE-2023-45282

Description

Prototype pollution in Open MCT before 3.1.0 via import action allows attackers to modify object prototypes, potentially leading to remote code execution.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Prototype pollution in Open MCT before 3.1.0 via import action allows attackers to modify object prototypes, potentially leading to remote code execution.

NASA Open MCT (Open Mission Control Technologies) is a web-based mission control framework for visualizing telemetry data. In versions prior to 3.1.0, the application is vulnerable to prototype pollution via the import action. Prototype pollution is a JavaScript-specific vulnerability where an attacker can inject properties into an object's prototype (__proto__), altering the behavior of all objects that inherit from that prototype [1].

Attackers can exploit this by crafting malicious JSON or JavaScript objects that, when imported into Open MCT, pollute the global object prototype. The import action does not properly sanitize or validate the input, allowing recursive merging of user-supplied objects with the application's objects [1][3]. This can be achieved without authentication if the import feature is exposed to unauthenticated users, or by a low-privileged user in authenticated scenarios.

Successful exploitation can lead to arbitrary property injection, which may alter the application's business logic. In a worst-case scenario, if server-side JavaScript processing occurs (e.g., during server-side rendering or API handling), this can escalate to remote code execution [1][4]. The impact depends on how Open MCT is deployed and the privileges of the affected process.

The vulnerability has been addressed in Open MCT version 3.1.0. The fix involves sanitizing input during the import process to prevent prototype pollution [3]. Users are strongly advised to upgrade to 3.1.0 or later. No workaround is available for prior versions.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openmctnpm
<= 3.0.2

Affected products

2

Patches

1
2243381d527c

Protect against prototype pollution in import action (#7094)

https://github.com/nasa/openmctDavid TsayOct 2, 2023via ghsa
5 files changed · +113 34
  • src/plugins/importFromJSONAction/ImportFromJSONAction.js+5 2 modified
    @@ -21,6 +21,7 @@
      *****************************************************************************/
     
     import objectUtils from 'objectUtils';
    +import { filter__proto__ } from 'utils/sanitization';
     import { v4 as uuid } from 'uuid';
     
     export default class ImportAsJSONAction {
    @@ -71,8 +72,10 @@ export default class ImportAsJSONAction {
     
       onSave(object, changes) {
         const selectFile = changes.selectFile;
    -    const objectTree = selectFile.body;
    -    this._importObjectTree(object, JSON.parse(objectTree));
    +    const jsonTree = selectFile.body;
    +    const objectTree = JSON.parse(jsonTree, filter__proto__);
    +
    +    this._importObjectTree(object, objectTree);
       }
     
       /**
    
  • src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js+54 31 modified
    @@ -22,10 +22,10 @@
     
     import { createOpenMct, resetApplicationState } from 'utils/testing';
     
    -import ImportFromJSONAction from './ImportFromJSONAction';
    -
     let openmct;
     let importFromJSONAction;
    +let folderObject;
    +let unObserve;
     
     describe('The import JSON action', function () {
       beforeEach((done) => {
    @@ -34,19 +34,8 @@ describe('The import JSON action', function () {
         openmct.on('start', done);
         openmct.startHeadless();
     
    -    importFromJSONAction = new ImportFromJSONAction(openmct);
    -  });
    -
    -  afterEach(() => {
    -    return resetApplicationState(openmct);
    -  });
    -
    -  it('has import as JSON action', () => {
    -    expect(importFromJSONAction.key).toBe('import.JSON');
    -  });
    -
    -  it('applies to return true for objects with composition', function () {
    -    const domainObject = {
    +    importFromJSONAction = openmct.actions.getAction('import.JSON');
    +    folderObject = {
           composition: [],
           name: 'Unnamed Folder',
           type: 'folder',
    @@ -59,8 +48,23 @@ describe('The import JSON action', function () {
             key: '84438cda-a071-48d1-b9bf-d77bd53e59ba'
           }
         };
    +  });
     
    -    const objectPath = [domainObject];
    +  afterEach(() => {
    +    importFromJSONAction = undefined;
    +    folderObject = undefined;
    +    unObserve?.();
    +    unObserve = undefined;
    +
    +    return resetApplicationState(openmct);
    +  });
    +
    +  it('has import as JSON action', () => {
    +    expect(importFromJSONAction).toBeDefined();
    +  });
    +
    +  it('applies to return true for objects with composition', function () {
    +    const objectPath = [folderObject];
     
         spyOn(openmct.composition, 'get').and.returnValue(true);
     
    @@ -97,26 +101,45 @@ describe('The import JSON action', function () {
       });
     
       it('calls showForm on invoke ', function () {
    -    const domainObject = {
    -      composition: [],
    -      name: 'Unnamed Folder',
    -      type: 'folder',
    -      location: '9f6c9dae-51c3-401d-92f1-c812de942922',
    -      modified: 1637021471624,
    -      persisted: 1637021471624,
    -      id: '84438cda-a071-48d1-b9bf-d77bd53e59ba',
    -      identifier: {
    -        namespace: '',
    -        key: '84438cda-a071-48d1-b9bf-d77bd53e59ba'
    -      }
    -    };
    -
    -    const objectPath = [domainObject];
    +    const objectPath = [folderObject];
     
         spyOn(openmct.forms, 'showForm').and.returnValue(Promise.resolve({}));
         spyOn(importFromJSONAction, 'onSave').and.returnValue(Promise.resolve({}));
         importFromJSONAction.invoke(objectPath);
     
         expect(openmct.forms.showForm).toHaveBeenCalled();
       });
    +
    +  it('protects against prototype pollution', (done) => {
    +    spyOn(console, 'warn');
    +    spyOn(openmct.forms, 'showForm').and.callFake(returnResponseWithPrototypePollution);
    +
    +    unObserve = openmct.objects.observe(folderObject, '*', callback);
    +
    +    importFromJSONAction.invoke([folderObject]);
    +
    +    function callback(newObject) {
    +      const hasPollutedProto =
    +        Object.prototype.hasOwnProperty.call(newObject, '__proto__') ||
    +        Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(newObject), 'toString');
    +
    +      // warning from openmct.objects.get
    +      expect(console.warn).not.toHaveBeenCalled();
    +      expect(hasPollutedProto).toBeFalse();
    +
    +      done();
    +    }
    +
    +    function returnResponseWithPrototypePollution() {
    +      const pollutedResponse = {
    +        selectFile: {
    +          name: 'imported object',
    +          // eslint-disable-next-line prettier/prettier
    +          body: "{\"openmct\":{\"c28d230d-e909-4a3e-9840-d9ef469dda70\":{\"identifier\":{\"key\":\"c28d230d-e909-4a3e-9840-d9ef469dda70\",\"namespace\":\"\"},\"name\":\"Unnamed Overlay Plot\",\"type\":\"telemetry.plot.overlay\",\"composition\":[],\"configuration\":{\"series\":[]},\"modified\":1695837546833,\"location\":\"mine\",\"created\":1695837546833,\"persisted\":1695837546833,\"__proto__\":{\"toString\":\"foobar\"}}},\"rootId\":\"c28d230d-e909-4a3e-9840-d9ef469dda70\"}"
    +        }
    +      };
    +
    +      return Promise.resolve(pollutedResponse);
    +    }
    +  });
     });
    
  • src/plugins/localStorage/LocalStorageObjectProvider.js+3 1 modified
    @@ -20,6 +20,8 @@
      * at runtime from the About dialog for additional information.
      *****************************************************************************/
     
    +import { filter__proto__ } from '../../utils/sanitization';
    +
     export default class LocalStorageObjectProvider {
       constructor(spaceKey = 'mct') {
         this.localStorage = window.localStorage;
    @@ -83,7 +85,7 @@ export default class LocalStorageObjectProvider {
        * @private
        */
       getSpaceAsObject() {
    -    return JSON.parse(this.getSpace());
    +    return JSON.parse(this.getSpace(), filter__proto__);
       }
     
       /**
    
  • src/plugins/localStorage/pluginSpec.js+22 0 modified
    @@ -73,6 +73,28 @@ describe('The local storage plugin', () => {
         expect(testObject.anotherProperty).toEqual(domainObject.anotherProperty);
       });
     
    +  it('prevents prototype pollution from manipulated localstorage', async () => {
    +    spyOn(console, 'warn');
    +
    +    const identifier = {
    +      namespace: '',
    +      key: 'test-key'
    +    };
    +
    +    const pollutedSpaceString = `{"test-key":{"__proto__":{"toString":"foobar"},"type":"folder","name":"A test object","identifier":{"namespace":"","key":"test-key"}}}`;
    +    getLocalStorage()[space] = pollutedSpaceString;
    +
    +    let testObject = await openmct.objects.get(identifier);
    +
    +    const hasPollutedProto =
    +      Object.prototype.hasOwnProperty.call(testObject, '__proto__') ||
    +      Object.getPrototypeOf(testObject) !== Object.getPrototypeOf({});
    +
    +    // warning from openmct.objects.get
    +    expect(console.warn).not.toHaveBeenCalled();
    +    expect(hasPollutedProto).toBeFalse();
    +  });
    +
       afterEach(() => {
         resetApplicationState(openmct);
         resetLocalStorage();
    
  • src/utils/sanitization.js+29 0 added
    @@ -0,0 +1,29 @@
    +/*****************************************************************************
    + * Open MCT, Copyright (c) 2014-2023, United States Government
    + * as represented by the Administrator of the National Aeronautics and Space
    + * Administration. All rights reserved.
    + *
    + * Open MCT is licensed under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + * http://www.apache.org/licenses/LICENSE-2.0.
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    + * License for the specific language governing permissions and limitations
    + * under the License.
    + *
    + * Open MCT includes source code licensed under additional open source
    + * licenses. See the Open Source Licenses file (LICENSES.md) included with
    + * this source code distribution or the Licensing information page available
    + * at runtime from the About dialog for additional information.
    + *****************************************************************************/
    +
    +function filter__proto__(key, value) {
    +  if (key !== '__proto__') {
    +    return value;
    +  }
    +}
    +
    +export { filter__proto__ };
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.