VYPR
Critical severity9.8NVD Advisory· Published Aug 12, 2024· Updated Apr 15, 2026

CVE-2024-38989

CVE-2024-38989

Description

izatop bunt v0.29.19 was discovered to contain a prototype pollution via the component /esm/qs.js. This vulnerability allows attackers to execute arbitrary code or cause a Denial of Service (DoS) via injecting arbitrary properties.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@bunt/appnpm
< 0.29.260.29.26

Patches

1
c55201a8cee0

fix: closes #27

https://github.com/izatop/buntabirMar 17, 2024via ghsa
3 files changed · +50 23
  • packages/app/src/TransformRequest/MultipartFormDataTransform.ts+4 5 modified
    @@ -17,10 +17,9 @@ export const MultipartFormDataTransform = async <T = unknown>(request: IRequest)
     
         const rs = await request.createReadableStream();
         const defer = new Defer<void>();
    -    const result: Record<string, any> = {};
         const pending: Defer<void>[] = [];
     
    -    const {parseFieldName, inject} = QueryString;
    +    const qs = new QueryString();
     
         bb
             .on("file", (name, file, info) => {
    @@ -37,7 +36,7 @@ export const MultipartFormDataTransform = async <T = unknown>(request: IRequest)
                     tmpname,
                 };
     
    -            inject(parseFieldName(name), value, result);
    +            qs.push(name, value);
     
                 const def = new Defer<void>();
                 pending.push(def);
    @@ -48,7 +47,7 @@ export const MultipartFormDataTransform = async <T = unknown>(request: IRequest)
             })
             .on("field", (name, value) => {
                 try {
    -                inject(parseFieldName(name), JSON.parse(value), result);
    +                qs.push(name, JSON.parse(value));
                 } catch {
                     // skip
                 }
    @@ -60,5 +59,5 @@ export const MultipartFormDataTransform = async <T = unknown>(request: IRequest)
         await defer;
         await Promise.all(pending);
     
    -    return result as any;
    +    return qs.toObject();
     };
    
  • packages/util/src/qs.ts+23 4 modified
    @@ -1,15 +1,34 @@
    +import {Rec} from "@bunt/type";
    +
     const isNumeric = (key: string): boolean => !isNaN(+key);
     
     export class QueryString {
    -    public static parseFieldName = (name: string): string[] => {
    +    readonly #value: Rec;
    +
    +    constructor(entries: [field: string, value: unknown][] = []) {
    +        this.#value = Object.create(null);
    +        for (const [field, value] of entries) {
    +            this.push(field, value);
    +        }
    +    }
    +
    +    public parseField(name: string): string[] {
             const base = name.replace(/\[.+/, "");
     
             return [base, ...[...name.matchAll(/\[([^\]]*)\]/ig)].map(([, key]) => key)];
    -    };
    +    }
    +
    +    public push(name: string, value: unknown): Rec {
    +        return this.#inject(this.parseField(name), value, this.#value);
    +    }
    +
    +    public toObject(): Rec {
    +        return this.#value;
    +    }
     
    -    public static inject = ([key, ...paths]: string[], value: unknown, fields: any = {}): any => {
    +    #inject = ([key, ...paths]: string[], value: unknown, fields: Rec = Object.create(null)): Rec => {
             if (paths.length > 0) {
    -            fields[key] = this.inject(paths, value, fields[key]);
    +            fields[key] = this.#inject(paths, value, fields[key]);
             } else {
                 fields[key] = value;
             }
    
  • packages/util/test/src/qs/QueryStirng.test.ts+23 14 modified
    @@ -1,57 +1,66 @@
     import {QueryString} from "../../../src";
     
     describe("QueryString", () => {
    +    test("Prevent pollution", () => {
    +        const injectTest = {};
    +        const injectKey = "__proto__[polluted]";
    +
    +        const qs = new QueryString();
    +        qs.push(injectKey, true);
    +
    +        expect(Reflect.has(Reflect.get(injectTest, "__proto__"), "polluted")).toBeFalsy();
    +    });
    +
         test("Base", () => {
    +        const qs = new QueryString();
             const field = "foo[bar][baz][0]";
    -        const parsed = QueryString.parseFieldName(field);
    +        const parsed = qs.parseField(field);
     
             expect(parsed)
                 .toEqual(["foo", "bar", "baz", "0"]);
     
    -        expect(QueryString.inject(parsed, 1))
    +        expect(qs.push(field, 1))
                 .toEqual({foo: {bar: {baz: [1]}}});
         });
     
         test("Array", () => {
    +        const qs = new QueryString();
             const map: [string, any][] = [
                 ["foo[0]", 1],
                 ["foo[1]", 2],
                 ["foo[2]", 3],
             ];
     
    -        const result = {};
             for (const [field, value] of map) {
    -            const paths = QueryString.parseFieldName(field);
    -            QueryString.inject(paths, value, result);
    +            qs.push(field, value);
             }
     
    -        expect(result).toEqual({foo: [1, 2, 3]});
    +        expect(qs.toObject()).toEqual({foo: [1, 2, 3]});
         });
     
         test("Nested array", () => {
    +        const qs = new QueryString();
             const map: [string, any][] = [
                 ["foo[0][a]", 1],
                 ["foo[0][b]", 2],
                 ["foo[1][c]", 3],
                 ["foo[1][d]", 4],
             ];
     
    -        const result = {};
             for (const [field, value] of map) {
    -            const paths = QueryString.parseFieldName(field);
    -            QueryString.inject(paths, value, result);
    +            qs.push(field, value);
             }
     
    -        expect(result).toEqual({foo: [{a: 1, b: 2}, {c: 3, d: 4}]});
    +        expect(qs.toObject()).toEqual({foo: [{a: 1, b: 2}, {c: 3, d: 4}]});
         });
     
         test.each([
             ["foo", 1, {foo: 1}],
             ["foo[bar]", 1, {foo: {bar: 1}}],
             ["foo[bar][0]", 1, {foo: {bar: [1]}}],
             ["foo[0][bar]", 1, {foo: [{bar: 1}]}],
    -    ])("Variants", (field, value, res) => (
    -        expect(QueryString.inject(QueryString.parseFieldName(field), value))
    -            .toEqual(res)
    -    ));
    +    ])("Variants", (field, value, res) => {
    +        const qs = new QueryString([[field, value]]);
    +        expect(qs.toObject()).toEqual(res);
    +    });
     });
    

Vulnerability mechanics

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

References

5

News mentions

0

No linked articles in our index yet.