Disallowing path manipulation attempt

Hello,
We recently bumped dependencies and are getting this error in our staging env.
I tried to find anything on the topic but could not.
Any help where to start would really be appreciated.

Something went wrong
Trace ID: 000000000000000099c90818591d36d8
There was an error invoking the function - Disallowing path manipulation attempt

Error: Disallowing path manipulation attempt
    at escapeParameter (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:740:23)
    at route (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:767:19)
    at _callee$ (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:56679:75)
    at tryCatch (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:56666:1062)
    at Generator.<anonymous> (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:56666:3012)
    at Generator.next (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:56666:1699)
    at asyncGeneratorStep (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:56668:103)
    at _next (/tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:56669:194)
    at /tmp/tunnel-7-aJnqlPs5QVHp/ProtectedFieldHandler.js:56669:364
    at new Promise (<anonymous>)

We simply get data using this small code:

const getDataFromJira = async (url) => {
  try {
    const response = await api.asUser().requestJira(route`${url}`);
    return await response.json();
  } catch (error) {
    console.log("getDataFromJira error: ", error);
    throw error;
  }
};

Can you tell us what is in the url variable you’re sending into the route function?

The route function is meant to be used at the point where you are constructing the URL from its parts, incorporating the user input into it. For example:

const url = route`/rest/api/3/issue/${issueKey}`;
await api.asUser().requestJira(url);

Instead of:

const url_bad = `/rest/api/3/issue/${issueKey}`;  // bad
await api.asUser().requestJira(route`${url_bad}`);

When route is called, it can check for possible path manipulation attempts (e.g. issueKey coming from the user as ../../../evil_api_call) and escape or block them properly.

If URL is constructed separately, route has no way of knowing which parts might have been manipulated by the user, so it might throw this error as a false positive.

I suggest rewriting your getDataFromJira function to accept the result of calling route, and move the route wherever the URLs are being constructed.

3 Likes

Thanks a lot for the answer, that was the problem, this seems to been allowed before but checking the change log, there has been some updates around this CWE. Great answer!

Above solution is not working for me. Any other solutions?

Sorry to hear that. Can you please show your code and what is happening?

Yeah sure. thanks for the reply

Defining resolver at the backend part

resolver.define("MAKE_API", async (req) => {
    const { url, method, body } = req?.payload;
    const options = {
        method: method,
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        body: JSON.stringify(body)
    }
    const apiUrl = route`${url}`
    const response = await api?.asUser()?.requestJira(apiUrl, options);
    const data = await response?.json();
    return data ? data : [];
})

Below is the code where i am invoking the resolver from frontend side

const GET_CURRENT_USER = async () => {
  const result = await invoke('GET_INFO', { key: '__info' })
  const response = await invoke('MAKE_API', {
    url: `${USER}?accountId=${result?.user?.id}`,     // Here USER = '/rest/api/3/user'
    method: 'GET',
    body: undefined
  })
  return response
}

@GaneshXDev First of all, you don’t need to use invoke for this code as you can call requestJira from the frontend directly.

If you have more involved logic, please follow the advice above:

  • Only pass the necessary information to the backend (in your case result.user.id).
  • Construct only the endpoint you actually need on the backend, starting with /rest/api/3/user.

Otherwise a malicious user can manipulate the url into an entirely different call with unintended effects.

1 Like

Thanks @AlexeyKotlyarov , Better I will go with requestJira from frontend side.

Hi @AlexeyKotlyarov

In terms of calling requestJira directly instead of using invoke does this apply also to older versions of api/ui? In addition, will the use of invoke trigger the path manipulation error?

Many thanks

Rob

@RobertHarkin Best create a new thread with your specific problem - I’ll answer but I can only guess what your use case is.

[does] calling requestJira […] apply to older versions of api/ui?

If you want to contact Jira from the UI, it’s easier to do directly and you should do so! But also update to the latest @forge/api as that has all the recent bugfixes and enhancements.

Will the use of invoke trigger the path manipulation error?

Not by itself. In the original question the problem was calling route on user-supplied input.

1 Like

A side note, you can use

import { assumeTrustedRoute } from '@forge/api'

whose signature is

export declare function assumeTrustedRoute(route: string): Route;

So get a route from a string.

:warning: As stated in the name of the function you assume the string route can be trusted !

Thanks @AlexeyKotlyarov. This post helped me resolve my issues.

@FabienLydoire thanks for this, yes I was aware but wanted to avoid using this. Ended up refactoring all of my API calls and they are now compliant and a lot neater than what I inherited.

Thanks again all!

1 Like