-
Notifications
You must be signed in to change notification settings - Fork 40
Feature/72 #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Feature/72 #86
Conversation
… works with implicit grant results.
…vironment file path.
…detail page tests for all token types.
| } catch(e) { | ||
| log.error("An error occurred while retrieving the claim description XML: " + e.stack); | ||
| res.status(500) | ||
| .render('error', { error: e }); |
Check warning
Code scanning / CodeQL
Information exposure through a stack trace Medium
stack trace information
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 hour ago
To fix the information exposure via stack trace, we should ensure that information sent to the user contains only generic error details, while any detailed error logs (including stack traces) are retained only in server logs. Specifically:
- In file
api/server.js, at line 119, replaceres.render('error', { error: e });with a more generic response. - Log the complete error (including stack trace) server-side using
log.error, as is already done. - For the user response, send only a general message, such as
"An unexpected error occurred."or a structured error object containing a generic message and sanitized code, but never expose stack traces or internal exception details. - If standardized error codes or models are used (such as Swagger), ensure the structure matches expectations, but content remains generic.
No change to imports is required; use only standard logging.
-
Copy modified lines R119-R123
| @@ -116,7 +116,11 @@ | ||
| } catch(e) { | ||
| log.error("An error occurred while retrieving the claim description XML: " + e.stack); | ||
| res.status(STATUS_500) | ||
| .render('error', { error: e }); | ||
| .json({ | ||
| status: false, | ||
| code: 'UNEXPECTED_ERROR', | ||
| message: 'An unexpected error occurred.' | ||
| }); | ||
| } | ||
| }); | ||
|
|
…rom token_detail or userinfo views. Userinfo has a backend processing option and works correctly with POST method.
…h call and validate the tokens received.
| axios({ | ||
| method: 'get', | ||
| url: Buffer.from(req.query.userinfo_endpoint, 'base64').toString('utf-8'), | ||
| headers: headers, | ||
| httpsAgent: new (require('https').Agent)({ rejectUnauthorized: true }) | ||
| }) |
Check failure
Code scanning / CodeQL
Server-side request forgery Critical
URL
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 hour ago
To fix the SSRF issue, we need to ensure the outgoing request’s URL cannot be arbitrarily set by the user. The most robust standard fix is to restrict the target host/domain with an allow-list, so that only pre-approved endpoints can be used. For example, instead of using any URL provided by the user, the code should map a user-supplied identifier (such as an alias or region code) to a specific endpoint from a list of known safe URLs. If the architecture requires the full URL, then strict validation (e.g., regex, hostname checking, path disallowing local/internal services) must be implemented.
In this case, we should:
- Define an allow-list of permitted endpoints (e.g., in an object mapping of endpoint aliases to URLs).
- Accept from the client only either: (a) an alias key that selects the real endpoint, or (b) a base64 value and then check the decoded URL against the allow-list.
- If the value does not match any allowed endpoint, reject the request with an error status and message.
Steps (in file api/server.js):
- Add a mapping (object) of allowed endpoints (e.g., OIDC issuers’ userinfo URLs).
- In
userinfo_common, fetch either an alias or a URL, decode it if needed, and check that it matches the allowed endpoints. - If valid, proceed with the request. If not, respond with 400 (Bad Request) and error message.
- Optionally, log invalid attempts for audit.
- No additional imports are necessary.
-
Copy modified lines R334-R340 -
Copy modified lines R342-R389 -
Copy modified line R391 -
Copy modified line R393
| @@ -331,51 +331,66 @@ | ||
| log.debug("Leaving app.get for /userinfo."); | ||
| }); | ||
|
|
||
| // Allowed OIDC UserInfo endpoints (replace with your valid endpoints) | ||
| const ALLOWED_USERINFO_ENDPOINTS = [ | ||
| "https://accounts.google.com/o/oauth2/v3/userinfo", | ||
| "https://login.microsoftonline.com/common/openid/userinfo", | ||
| // Add more allowed endpoints here | ||
| ]; | ||
|
|
||
| function userinfo_common(req, res) { | ||
| try { | ||
| log.info('Entering app.get for /userinfo.'); | ||
| var headers = { | ||
| "Authorization": req.headers.authorization, | ||
| }; | ||
| // All types of requests are converted to GET. | ||
| log.debug("Method: GET"); | ||
| log.debug("URL: " + Buffer.from(req.query.userinfo_endpoint, 'base64').toString('utf-8')); | ||
| log.debug("headers: " + JSON.stringify(headers)); | ||
| axios({ | ||
| method: 'get', | ||
| url: Buffer.from(req.query.userinfo_endpoint, 'base64').toString('utf-8'), | ||
| headers: headers, | ||
| httpsAgent: new (require('https').Agent)({ rejectUnauthorized: true }) | ||
| }) | ||
| .then(function (response) { | ||
| log.debug('Response from OIDC UserInfo Endpoint: ' + JSON.stringify(response.data)); | ||
| log.debug('Headers: ' + response.headers); | ||
| res.status(response.status); | ||
| res.json(response.data); | ||
| }) | ||
| .catch(function (error) { | ||
| log.error('Error from OIDC UserInfo Endpoint: ' + error); | ||
| if(!!error.response) { | ||
| if(!!error.response.status) { | ||
| log.error("Error Status: " + error.response.status); | ||
| try { | ||
| log.info('Entering app.get for /userinfo.'); | ||
| var headers = { | ||
| "Authorization": req.headers.authorization, | ||
| }; | ||
| // All types of requests are converted to GET. | ||
| log.debug("Method: GET"); | ||
| let decodedUrl = Buffer.from(req.query.userinfo_endpoint, 'base64').toString('utf-8'); | ||
| log.debug("Decoded userinfo_endpoint: " + decodedUrl); | ||
| log.debug("headers: " + JSON.stringify(headers)); | ||
|
|
||
| if (!ALLOWED_USERINFO_ENDPOINTS.includes(decodedUrl)) { | ||
| log.error("Denied userinfo_endpoint: " + decodedUrl); | ||
| res.status(400).json({ error: "Requested endpoint is not allowed." }); | ||
| return; | ||
| } | ||
|
|
||
| axios({ | ||
| method: 'get', | ||
| url: decodedUrl, | ||
| headers: headers, | ||
| httpsAgent: new (require('https').Agent)({ rejectUnauthorized: true }) | ||
| }) | ||
| .then(function (response) { | ||
| log.debug('Response from OIDC UserInfo Endpoint: ' + JSON.stringify(response.data)); | ||
| log.debug('Headers: ' + response.headers); | ||
| res.status(response.status); | ||
| res.json(response.data); | ||
| }) | ||
| .catch(function (error) { | ||
| log.error('Error from OIDC UserInfo Endpoint: ' + error); | ||
| if(!!error.response) { | ||
| if(!!error.response.status) { | ||
| log.error("Error Status: " + error.response.status); | ||
| } | ||
| if(!!error.response.data) { | ||
| log.error("Error Response body: " + JSON.stringify(error.response.data)); | ||
| } | ||
| if(!!error.response.headers) { | ||
| log.error("Error Response headers: " + error.response.headers); | ||
| } | ||
| if (!!error.response) { | ||
| res.status(error.response.status); | ||
| res.json(error.response.data); | ||
| } else { | ||
| res.status(STATUS_500); | ||
| res.json(error.message); | ||
| } | ||
| } | ||
| if(!!error.response.data) { | ||
| log.error("Error Response body: " + JSON.stringify(error.response.data)); | ||
| } | ||
| if(!!error.response.headers) { | ||
| log.error("Error Response headers: " + error.response.headers); | ||
| } | ||
| if (!!error.response) { | ||
| res.status(error.response.status); | ||
| res.json(error.response.data); | ||
| } else { | ||
| res.status(STATUS_500); | ||
| res.json(error.message); | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
| } catch(e) { | ||
| log.error("Error from OIDC UserInfo Endpoint: " + error); | ||
| log.error("Error from OIDC UserInfo Endpoint: " + e); | ||
| } | ||
| } | ||
|
|
|
|
||
| app.use(bodyParser.json()); | ||
| var corsOptions = { | ||
| origin: '*', |
Check warning
Code scanning / CodeQL
Permissive CORS configuration Medium
OAuth2 Implicit Grant + Test working.