VYPR
Critical severityOSV Advisory· Published Jul 25, 2022· Updated Sep 16, 2024

Prototype Pollution

CVE-2020-28461

Description

This affects the package js-ini before 1.3.0. If an attacker submits a malicious INI file to an application that parses it with parse , they will pollute the prototype on the application. This can be exploited further depending on the context.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
js-ininpm
< 1.3.01.3.0

Affected products

1

Patches

1
fa17efb7e3a7

refactoring and new functional

https://github.com/Sdju/js-inizedeDec 22, 2020via ghsa
12 files changed · +104 15
  • package.json+2 2 modified
    @@ -38,7 +38,7 @@
         "husky": "^4.2.3",
         "jest": "^22.4.3",
         "ts-jest": "^22.4.2",
    -    "typescript": "3.4.3"
    +    "typescript": "^4.1.3"
       },
       "jest": {
         "globals": {
    @@ -50,7 +50,7 @@
           "^.+\\.(ts|tsx)$": "ts-jest"
         },
         "testMatch": [
    -      "**/test/*.+(ts|tsx|js)"
    +      "**/test/**/*.+(ts|tsx|js)"
         ],
         "moduleFileExtensions": [
           "ts",
    
  • package-lock.json+4 4 modified
    @@ -1,6 +1,6 @@
     {
       "name": "js-ini",
    -  "version": "1.1.5",
    +  "version": "1.2.0",
       "lockfileVersion": 1,
       "requires": true,
       "dependencies": {
    @@ -8362,9 +8362,9 @@
           "dev": true
         },
         "typescript": {
    -      "version": "3.4.3",
    -      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.3.tgz",
    -      "integrity": "sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ==",
    +      "version": "4.1.3",
    +      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
    +      "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
           "dev": true
         },
         "uglify-js": {
    
  • src/errors/errors-symbol.ts+1 0 added
    @@ -0,0 +1 @@
    +export const $Errors: unique symbol = Symbol('Errors of parsing');
    
  • src/errors/index.ts+3 0 added
    @@ -0,0 +1,3 @@
    +export * from './parsing-error';
    +export * from './proto-error';
    +export * from './errors-symbol';
    
  • src/errors/parsing-error.ts+1 3 renamed
    @@ -1,8 +1,6 @@
    -export const $Errors: unique symbol = Symbol('Errors of parsing');
    -
     export class ParsingError extends Error {
       constructor(line: string, lineNumber: number) {
    -    super(`Unsupported type of line: [${lineNumber}]"${line}"`);
    +    super(`Unsupported type of line: [${lineNumber}] "${line}"`);
         this.line = line;
         this.lineNumber = lineNumber;
       }
    
  • src/errors/proto-error.ts+8 0 added
    @@ -0,0 +1,8 @@
    +export class ProtoError extends Error {
    +  constructor(lineNumber: number) {
    +    super(`Unsupported section name "__proto__": [${lineNumber}]"`);
    +    this.lineNumber = lineNumber;
    +  }
    +
    +  public lineNumber: number;
    +}
    
  • src/index.ts+1 0 modified
    @@ -1,3 +1,4 @@
     export * from './errors';
     export * from './parse';
     export * from './stringify';
    +export * from './proto';
    
  • src/interfaces/custom-typing.ts+1 1 modified
    @@ -1,3 +1,3 @@
     export interface ICustomTyping {
    -  (val: string, section: string, key: string): any
    +  (val: string, section: string | symbol, key: string): any
     }
    
  • src/interfaces/ini-object.ts+2 0 modified
    @@ -1,6 +1,8 @@
     import { $Errors, ParsingError } from '../errors';
     import { IIniObjectSection } from './ini-object-section';
    +import { $Proto } from '../proto';
     
     export interface IIniObject extends IIniObjectSection {
       [$Errors]?: ParsingError[];
    +  [$Proto]?: IIniObjectSection;
     }
    
  • src/parse.ts+16 2 modified
    @@ -1,16 +1,22 @@
    -import { $Errors, ParsingError } from './errors';
    +import {
    +  $Errors,
    +  ParsingError,
    +  ProtoError,
    +} from './errors';
     import { IIniObject } from './interfaces/ini-object';
     import { IniValue } from './types/ini-value';
     import { IIniObjectSection } from './interfaces/ini-object-section';
     import { autoType } from './helpers/auto-type';
     import { ICustomTyping } from './interfaces/custom-typing';
    +import { $Proto } from './proto';
     
     export interface IParseConfig {
       comment?: string;
       delimiter?: string;
       nothrow?: boolean;
       autoTyping?: boolean | ICustomTyping;
       dataSections?: string[];
    +  protoSymbol?: boolean;
     }
     
     const sectionNameRegex = /\[(.*)]$/;
    @@ -22,6 +28,7 @@ export function parse(data: string, params?: IParseConfig): IIniObject {
         nothrow = false,
         autoTyping = true,
         dataSections = [],
    +    protoSymbol = false,
       } = { ...params };
       let typeParser: ICustomTyping;
       if (typeof autoTyping === 'function') {
    @@ -45,9 +52,16 @@ export function parse(data: string, params?: IParseConfig): IIniObject {
           const match = line.match(sectionNameRegex);
           if (match !== null) {
             currentSection = match[1].trim();
    +        if (currentSection === '__proto__') {
    +          if (protoSymbol) {
    +            currentSection = <string><any> $Proto;
    +          } else {
    +            throw new ProtoError(lineNumber);
    +          }
    +        }
             isDataSection = dataSections.includes(currentSection);
             if (!(currentSection in result)) {
    -          result[currentSection] = (isDataSection) ? [] : {};
    +          result[currentSection] = (isDataSection) ? [] : Object.create(null);
             }
             continue;
           }
    
  • src/proto.ts+1 0 added
    @@ -0,0 +1 @@
    +export const $Proto: unique symbol = Symbol('__proto__');
    
  • test/tests.ts+64 3 modified
    @@ -1,27 +1,35 @@
    -import { parse, stringify } from '../src';
    +import {
    +  parse,
    +  stringify,
    +  $Proto,
    +  $Errors,
    +  ParsingError,
    +} from '../src';
     
     const ini1 = `v1 = 2
     v-2=true
     v 3 = string
     [smbd]
     v1=5
    -v2 = what 
    +v2 = what
     ;comment
     v5 = who is who = who
     
     [test scope with spaces]
     mgm*1  = 2.5`;
    +
     const ini2 = `v1 : 2
     v-2:true
     v 3 : string
     [smbd]
     v1:5
    -v2 : what 
    +v2 : what
     #comment
     v5 : who is who = who
     
     [test scope with spaces]
     mgm*1  : 2.5`;
    +
     const ini3 = `v1=2
     v-2=true
     v 3=string
    @@ -33,6 +41,7 @@ v5=who is who = who
     
     [test scope with spaces]
     mgm*1=2.5`;
    +
     const ini4 = `v1: 2
     v-2: true
     v 3: string
    @@ -42,6 +51,7 @@ v2: what
     v5: who is who = who
     [test scope with spaces]
     mgm*1: 2.5`;
    +
     const ini5 = `v1: 2
     v-2: true
     v 3: string
    @@ -53,6 +63,24 @@ v1: 5
     b1c,wdwd,15:68
     wx/w':wwdlw,:d,wld
     efkeofk`;
    +
    +const ini6 = `
    +[ __proto__  ]
    +polluted = "polluted"`;
    +
    +const ini7 = `
    +[scope with trash]
    +ok = value
    +trash
    +
    +[scope with only trash]
    +only trash
    +
    +[empty scope]
    +[normal scope]
    +ok = value
    +`;
    +
     const v1 = {
       v1: 2,
       'v-2': true,
    @@ -153,3 +181,36 @@ test('ini stringify', () => {
         autoTyping: false,
       })).toEqual(v3);
     });
    +
    +test('ini parsing: proto', () => {
    +  expect(() => parse(ini6))
    +    .toThrow('Unsupported section name "__proto__": [2]"');
    +
    +  expect(parse(ini6, { protoSymbol: true }))
    +    .toEqual({
    +      [$Proto]: {
    +        polluted: '"polluted"',
    +      },
    +    });
    +});
    +
    +test('ini parsing: errors', () => {
    +  expect(() => parse(ini7))
    +    .toThrow('Unsupported type of line: [4] "trash"');
    +
    +  expect(parse(ini7, { nothrow: true }))
    +    .toEqual({
    +      'scope with trash': {
    +        ok: 'value',
    +      },
    +      'scope with only trash': {},
    +      'empty scope': {},
    +      'normal scope': {
    +        ok: 'value',
    +      },
    +      [$Errors]: [
    +        new ParsingError('trash', 4),
    +        new ParsingError('only trash', 7),
    +      ],
    +    });
    +});
    

Vulnerability mechanics

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

References

4

News mentions

0

No linked articles in our index yet.