Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 21 additions & 73 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "azure-iot-explorer",
"version": "0.15.13",
"version": "0.15.14",
"description": "This project welcomes contributions and suggestions. Most contributions require you to agree to a\r Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\r the rights to use your contribution. For details, visit https://cla.microsoft.com.",
"main": "host/electron.js",
"build": {
Expand Down Expand Up @@ -111,14 +111,15 @@
"react-router-dom": "^5.2.0",
"react-toastify": "^4.4.0",
"redux-saga": "^1.1.3",
"request-filtering-agent": "^3.2.0",
"semver": "^6.3.1",
"ssrf-req-filter": "^1.1.1",
"typescript-fsa": "^3.0.0-beta-2",
"typescript-fsa-reducers": "^1.0.0",
"uuid": "^3.3.3",
"ws": "^8.17.1"
},
"devDependencies": {
"@electron/fuses": "^1.8.0",
"@redux-saga/testing-utils": "^1.1.3",
"@types/async-lock": "^1.1.0",
"@types/core-js": "^2.5.0",
Expand Down Expand Up @@ -148,7 +149,6 @@
"electron": "^22.3.25",
"electron-builder": "^26.0.0",
"electron-reload": "^2.0.0-alpha.1",
"@electron/fuses": "^1.8.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.1",
"enzyme-to-json": "^3.3.5",
Expand Down Expand Up @@ -214,7 +214,7 @@
]
},
"transformIgnorePatterns": [
"node_modules/(?!react-movable|request-filtering-agent|cheerio|htmlparser2|dom-serializer|domhandler|domutils|entities|css-select|css-what|boolbase|nth-check|parse5|parse5-htmlparser2-tree-adapter|@azure/event-hubs|@azure/core-amqp|@azure/core-auth|@azure/core-rest-pipeline|@azure/core-tracing|@azure/core-util|@azure/abort-controller|@azure/logger|@typespec/ts-http-runtime)"
"node_modules/(?!react-movable|cheerio|htmlparser2|dom-serializer|domhandler|domutils|entities|css-select|css-what|boolbase|nth-check|parse5|parse5-htmlparser2-tree-adapter|@azure/event-hubs|@azure/core-amqp|@azure/core-auth|@azure/core-rest-pipeline|@azure/core-tracing|@azure/core-util|@azure/abort-controller|@azure/logger|@typespec/ts-http-runtime)"
],
"testRegex": "(\\.|/)(spec)\\.(tsx?)$",
"moduleNameMapper": {
Expand Down
26 changes: 4 additions & 22 deletions public/handlers/dataPlaneHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License
**********************************************************/
import fetch, { Response } from 'node-fetch';
import * as ssrfFilter from 'ssrf-req-filter';
import {
validateAzureIoTHostname,
sanitizeHeaders,
Expand All @@ -14,22 +15,6 @@ import { DataPlaneRequest, DataPlaneResponse } from '../interfaces/deviceInterfa
const DEVICE_STATUS_HEADER = 'x-ms-command-statuscode';
const SERVER_ERROR = 500;

// Cache the dynamically imported module
let requestFilteringAgent: any = null; // tslint:disable-line:no-any

/**
* Dynamically import request-filtering-agent (ESM module)
* Uses Function constructor to prevent TypeScript from converting to require()
*/
const getRequestFilteringAgent = async () => {
if (!requestFilteringAgent) {
// Use Function constructor to create a true dynamic import that won't be transformed by TypeScript
const dynamicImport = new Function('specifier', 'return import(specifier)');
requestFilteringAgent = await dynamicImport('request-filtering-agent');
}
return requestFilteringAgent;
};

/**
* Handle data plane request via IPC
* This replaces the Express route handler
Expand Down Expand Up @@ -98,9 +83,6 @@ export const generateDataPlaneRequestBody = async (request: DataPlaneRequest) =>

const url = `https://${hostname}/${encodeURIComponent(path)}${queryString}`;

// Dynamically import ESM module for SSRF protection
const { useAgent } = await getRequestFilteringAgent();

return {
url,
request: {
Expand All @@ -109,9 +91,9 @@ export const generateDataPlaneRequestBody = async (request: DataPlaneRequest) =>
method: request.httpMethod.toUpperCase(),
redirect: 'error' as const, // Block all HTTP redirects (SSRF protection)
timeout: 30000, // 30 second timeout
// Use request-filtering-agent for SSRF protection
// Blocks requests to private IPs, loopback, link-local, IMDS, etc.
agent: useAgent(url),
// Use ssrf-req-filter for SSRF protection
// Blocks requests to private IPs, loopback, link-local, etc.
agent: ssrfFilter(url),
}
};
};
Expand Down
Loading