VYPR
Medium severity5.3NVD Advisory· Published Feb 14, 2025· Updated Apr 15, 2026

CVE-2025-25285

CVE-2025-25285

Description

@octokit/endpoint turns REST API endpoints into generic request options. Starting in version 4.1.0 and prior to version 10.1.3, by crafting specific options parameters, the endpoint.parse(options) call can be triggered, leading to a regular expression denial-of-service (ReDoS) attack. This causes the program to hang and results in high CPU utilization. The issue occurs in the parse function within the parse.ts file of the npm package @octokit/endpoint. Version 10.1.3 contains a patch for the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@octokit/endpointnpm
>= 9.0.5, < 9.0.69.0.6
@octokit/endpointnpm
>= 10.0.0, < 10.1.310.1.3

Patches

2
6c9c5be033c4

Merge commit from fork

https://github.com/octokit/endpoint.jsNick FloydFeb 13, 2025via ghsa
3 files changed · +93 3
  • src/parse.ts+1 1 modified
    @@ -59,7 +59,7 @@ export function parse(options: EndpointDefaults): RequestOptions {
         if (url.endsWith("/graphql")) {
           if (options.mediaType.previews?.length) {
             const previewsFromAcceptHeader =
    -          headers.accept.match(/[\w-]+(?=-preview)/g) || ([] as string[]);
    +          headers.accept.match(/(?<![\w-])[\w-]+(?=-preview)/g) || ([] as string[]);
             headers.accept = previewsFromAcceptHeader
               .concat(options.mediaType.previews!)
               .map((preview) => {
    
  • src/util/extract-url-variable-names.ts+2 2 modified
    @@ -1,7 +1,7 @@
    -const urlVariableRegex = /\{[^}]+\}/g;
    +const urlVariableRegex = /\{[^{}}]+\}/g;
     
     function removeNonChars(variableName: string) {
    -  return variableName.replace(/^\W+|\W+$/g, "").split(/,/);
    +  return variableName.replace(/(?:^\W+)|(?:(?<!\W)\W+$)/g, "").split(/,/);
     }
     
     export function extractUrlVariableNames(url: string) {
    
  • test/parse.test.ts+90 0 modified
    @@ -52,4 +52,94 @@ describe("endpoint.parse()", () => {
     
         expect(input.headers.accept).toEqual("application/vnd.github.v3+json");
       });
    +
    +  it("Test ReDoS - attack string #1", async () => {
    +    const startTime = performance.now();
    +    try {
    +      endpoint.parse({
    +        method: "POST",
    +        url: "/graphql", // Ensure that the URL ends with "/graphql"
    +        headers: {
    +          accept: "" + "A".repeat(100000) + "-", // Pass in the attack string
    +          "content-type": "text/plain",
    +          "user-agent": "Your User Agent String Here",
    +        },
    +        mediaType: {
    +          previews: ["test-preview"], // Ensure that mediaType.previews exists and has values
    +          format: "raw", // Optional media format
    +        },
    +        baseUrl: "https://api.github.com",
    +      });
    +    } catch (error) {
    +      // pass
    +    }
    +    const endTime = performance.now();
    +    const elapsedTime = endTime - startTime;
    +    const reDosThreshold = 2000; 
    +
    +    expect(elapsedTime).toBeLessThanOrEqual(reDosThreshold);
    +    if (elapsedTime > reDosThreshold) {
    +      console.warn(`🚨 Potential ReDoS Attack! getDuration method took ${elapsedTime.toFixed(2)} ms, exceeding threshold of ${reDosThreshold} ms.`);
    +    }
    +  });
    +
    +  it("Test ReDoS - attack string #2", async () => {
    +    const startTime = performance.now();
    +    try {
    +      endpoint.parse({
    +        method: "POST",
    +        url: "{".repeat(100000) + "@", // Pass in the attack string
    +        headers: {
    +          accept: "application/vnd.github.v3+json",
    +          "content-type": "text/plain",
    +          "user-agent": "Your User Agent String Here",
    +        },
    +        mediaType: {
    +          previews: ["test-preview"], // Ensure that mediaType.previews exists and has values
    +          format: "raw", // Optional media format
    +        },
    +        baseUrl: "https://api.github.com",
    +      });
    +    } catch (error) {
    +      // pass
    +    }
    +    const endTime = performance.now();
    +    const elapsedTime = endTime - startTime;
    +    const reDosThreshold = 2000; 
    +
    +    expect(elapsedTime).toBeLessThanOrEqual(reDosThreshold);
    +    if (elapsedTime > reDosThreshold) {
    +      console.warn(`🚨 Potential ReDoS Attack! getDuration method took ${elapsedTime.toFixed(2)} ms, exceeding threshold of ${reDosThreshold} ms.`);
    +    }
    +  });
    +
    +  it("Test ReDoS - attack string #3", async () => {
    +    const startTime = performance.now();
    +    try {
    +      endpoint.parse({
    +        method: "POST",
    +        url: "{"+"00"+"\u0000".repeat(100000)+"a!a"+"}", // Pass in the attack string
    +        headers: {
    +          accept: "application/vnd.github.v3+json",
    +          "content-type": "text/plain",
    +          "user-agent": "Your User Agent String Here",
    +        },
    +        mediaType: {
    +          previews: ["test-preview"],
    +          format: "raw",
    +        },
    +        baseUrl: "https://api.github.com",
    +      });
    +    } catch (error) {
    +      // pass
    +    }
    +    const endTime = performance.now();
    +    const elapsedTime = endTime - startTime;
    +    const reDosThreshold = 2000; 
    +
    +    expect(elapsedTime).toBeLessThanOrEqual(reDosThreshold);
    +    if (elapsedTime > reDosThreshold) {
    +      console.warn(`🚨 Potential ReDoS Attack! getDuration method took ${elapsedTime.toFixed(2)} ms, exceeding threshold of ${reDosThreshold} ms.`);
    +    }
    +  });
     });
    

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.