@oneuptime/common 12.0.11 → 12.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
- package/Models/DatabaseModels/NetworkDevice.ts +64 -0
- package/Models/DatabaseModels/StatusPage.ts +59 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
- package/Server/API/AIChatAPI.ts +55 -56
- package/Server/API/CommonAPI.ts +89 -0
- package/Server/API/MicrosoftTeamsAPI.ts +20 -9
- package/Server/API/SlackAPI.ts +13 -10
- package/Server/API/StatusPageAPI.ts +2197 -2128
- package/Server/API/TelemetryAPI.ts +72 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +11 -2
- package/Server/Services/ApiKeyPermissionService.ts +116 -2
- package/Server/Services/DatabaseService.ts +17 -6
- package/Server/Services/LogAggregationService.ts +34 -0
- package/Server/Services/LogService.ts +21 -0
- package/Server/Services/NetworkSiteService.ts +12 -0
- package/Server/Services/SpanService.ts +21 -0
- package/Server/Services/TraceAggregationService.ts +15 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +9 -0
- package/Server/Types/Workflow/Components/JavaScript.ts +9 -0
- package/Server/Utils/APIKey/AccessPermission.ts +16 -40
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
- package/Server/Utils/LogRedaction.ts +576 -0
- package/Server/Utils/Logger.ts +123 -46
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +12 -0
- package/Server/Utils/Monitor/MonitorResource.ts +29 -9
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
- package/Server/Utils/StartServer.ts +30 -9
- package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
- package/Server/Utils/VM/VMRunner.ts +693 -874
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
- package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
- package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
- package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
- package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
- package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
- package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
- package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
- package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
- package/Tests/Server/Types/Workflow/Components/CustomCodeScriptError.test.ts +121 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorJavascriptExpression.test.ts +92 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
- package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
- package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
- package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
- package/Tests/Server/Utils/VM/VMRunnerIsolation.test.ts +385 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +3 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
- package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
- package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
- package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
- package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
- package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
- package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
- package/Tests/UI/Components/MasterPage.test.tsx +36 -1
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
- package/Types/API/Hostname.ts +79 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
- package/Types/Database/DatabaseProperty.ts +38 -0
- package/Types/Database/TableColumn.ts +34 -7
- package/Types/Events/Recurring.ts +16 -1
- package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +14 -2
- package/Types/Monitor/MonitorStep.ts +13 -1
- package/Types/Monitor/MonitorType.ts +345 -40
- package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
- package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
- package/UI/Components/CardSelect/CardSelect.tsx +714 -105
- package/UI/Components/EmptyState/EmptyState.tsx +8 -1
- package/UI/Components/Footer/Footer.tsx +32 -6
- package/UI/Components/Forms/Fields/FormField.tsx +3 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Loader/PageLoader.tsx +29 -8
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
- package/UI/Components/MasterPage/MasterPage.tsx +24 -11
- package/Utils/Monitor/MonitorMetricType.ts +16 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
- package/Utils/Telemetry/CrossSignalScope.ts +75 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +48 -35
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +59 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +13 -10
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +745 -673
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +34 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +15 -6
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogService.js +16 -0
- package/build/dist/Server/Services/LogService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +12 -0
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/SpanService.js +16 -0
- package/build/dist/Server/Services/SpanService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +449 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Logger.js +98 -45
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -7
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js +402 -525
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +63 -0
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +38 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js +24 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +13 -1
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +9 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +330 -31
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/Footer/Footer.js +7 -4
- package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
- package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,272 +13,7 @@ import http from "http";
|
|
|
13
13
|
import https from "https";
|
|
14
14
|
import ivm from "isolated-vm";
|
|
15
15
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
16
|
-
import vm from "vm";
|
|
17
16
|
import SSRFProtection from "../SSRFProtection";
|
|
18
|
-
/**
|
|
19
|
-
* Symbol used to retrieve the real (unwrapped) target from a sandbox proxy.
|
|
20
|
-
* Hidden from user code via ownKeys / has traps.
|
|
21
|
-
*/
|
|
22
|
-
const PROXY_TARGET_SYMBOL = Symbol("sandboxProxyTarget");
|
|
23
|
-
/**
|
|
24
|
-
* Hardening prelude injected before user code in `runCodeInNodeVM`.
|
|
25
|
-
*
|
|
26
|
-
* Node's `vm` module is not a security boundary. The published PoC for
|
|
27
|
-
* GHSA-g9cp-35m2-fjv6 forces a stack-overflow `RangeError`, walks
|
|
28
|
-
* `e.__proto__.__proto__.__proto__` to `Object.prototype`, then reads
|
|
29
|
-
* `.toString.constructor` to obtain a `Function` constructor that compiles
|
|
30
|
-
* code in a realm where `process.binding('spawn_sync')` is reachable.
|
|
31
|
-
*
|
|
32
|
-
* This prelude closes that path by:
|
|
33
|
-
* - severing `Error.prototype`'s link to `Object.prototype` so the 3-level
|
|
34
|
-
* walk lands on `null` instead of `Object.prototype`;
|
|
35
|
-
* - deleting `.constructor` from every built-in prototype, so even a
|
|
36
|
-
* different walk (e.g. `(0).constructor.constructor`) cannot resolve to a
|
|
37
|
-
* function constructor;
|
|
38
|
-
* - clearing `Function` / `eval` from the sandbox global;
|
|
39
|
-
* - freezing the affected prototypes so user code cannot reattach them.
|
|
40
|
-
*
|
|
41
|
-
* This is a hotfix for the public PoC. The durable fix is to drop
|
|
42
|
-
* `runCodeInNodeVM` in favor of running synthetic monitor scripts in an
|
|
43
|
-
* out-of-process sandbox (tracked on the `probe-runner` branch).
|
|
44
|
-
*/
|
|
45
|
-
const VM_HARDENING_PRELUDE = `(() => {
|
|
46
|
-
const _ctors = [
|
|
47
|
-
Object, Function, Array, String, Number, Boolean, RegExp,
|
|
48
|
-
Error, RangeError, TypeError, SyntaxError, ReferenceError, EvalError, URIError,
|
|
49
|
-
Symbol, Date, Map, Set, WeakMap, WeakSet, Promise, Proxy,
|
|
50
|
-
ArrayBuffer, DataView,
|
|
51
|
-
Int8Array, Uint8Array, Uint8ClampedArray,
|
|
52
|
-
Int16Array, Uint16Array, Int32Array, Uint32Array,
|
|
53
|
-
Float32Array, Float64Array,
|
|
54
|
-
];
|
|
55
|
-
if (typeof BigInt !== 'undefined') _ctors.push(BigInt);
|
|
56
|
-
|
|
57
|
-
for (const C of _ctors) {
|
|
58
|
-
try { if (C && C.prototype) delete C.prototype.constructor; } catch (_) {}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Generator / async-function prototypes have no named global — reach via syntax.
|
|
62
|
-
try { delete Object.getPrototypeOf(function*(){}).constructor; } catch (_) {}
|
|
63
|
-
try { delete Object.getPrototypeOf(async function(){}).constructor; } catch (_) {}
|
|
64
|
-
try { delete Object.getPrototypeOf(async function*(){}).constructor; } catch (_) {}
|
|
65
|
-
|
|
66
|
-
try { Object.setPrototypeOf(Error.prototype, null); } catch (_) {}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
Object.defineProperty(globalThis, 'Function', {
|
|
70
|
-
value: undefined, writable: false, configurable: false,
|
|
71
|
-
});
|
|
72
|
-
} catch (_) {}
|
|
73
|
-
try {
|
|
74
|
-
Object.defineProperty(globalThis, 'eval', {
|
|
75
|
-
value: undefined, writable: false, configurable: false,
|
|
76
|
-
});
|
|
77
|
-
} catch (_) {}
|
|
78
|
-
|
|
79
|
-
for (const C of _ctors) {
|
|
80
|
-
try { if (C && C.prototype) Object.freeze(C.prototype); } catch (_) {}
|
|
81
|
-
}
|
|
82
|
-
})();`;
|
|
83
|
-
/** Properties blocked on every host-realm object exposed to the sandbox. */
|
|
84
|
-
const BLOCKED_SANDBOX_PROPERTIES = new Set([
|
|
85
|
-
"constructor",
|
|
86
|
-
"__proto__",
|
|
87
|
-
"prototype",
|
|
88
|
-
"mainModule",
|
|
89
|
-
/*
|
|
90
|
-
* Block Playwright methods that can spawn processes or access internals.
|
|
91
|
-
* Prevents RCE via browser.browserType().launch({executablePath:"/bin/sh"})
|
|
92
|
-
* and traversal via page.context().browser().browserType().launch(...)
|
|
93
|
-
*/
|
|
94
|
-
"browserType", // Browser → BrowserType (which has launch/connect)
|
|
95
|
-
"_browserType", // Internal alias for browserType — same escape vector
|
|
96
|
-
"launch", // BrowserType.launch() spawns a child process
|
|
97
|
-
"launchServer", // BrowserType.launchServer() spawns a browser server process
|
|
98
|
-
"launchPersistentContext", // BrowserType.launchPersistentContext() spawns a child process
|
|
99
|
-
"connectOverCDP", // BrowserType.connectOverCDP() connects via Chrome DevTools Protocol
|
|
100
|
-
"connect", // BrowserType.connect() connects to a remote browser
|
|
101
|
-
"newCDPSession", // BrowserContext/Page.newCDPSession() opens raw CDP sessions
|
|
102
|
-
]);
|
|
103
|
-
/**
|
|
104
|
-
* Wraps a host-realm value in a Proxy that blocks prototype-chain traversal.
|
|
105
|
-
* Primitives and null/undefined pass through unchanged.
|
|
106
|
-
* Object proxies are cached to preserve identity; function proxies are created
|
|
107
|
-
* per-access so they bind to the correct `this` (parent object).
|
|
108
|
-
*/
|
|
109
|
-
function createSandboxProxy(value, cache, parentObj) {
|
|
110
|
-
if (value === null || value === undefined) {
|
|
111
|
-
return value;
|
|
112
|
-
}
|
|
113
|
-
const valueType = typeof value;
|
|
114
|
-
if (valueType !== "object" && valueType !== "function") {
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
117
|
-
const target = value;
|
|
118
|
-
if (valueType === "function") {
|
|
119
|
-
/*
|
|
120
|
-
* Function proxies are NOT cached because the same function may be a method
|
|
121
|
-
* on different parent objects and needs a different `this` binding each time.
|
|
122
|
-
*/
|
|
123
|
-
const fnProxy = new Proxy(target, {
|
|
124
|
-
get(fnTarget, prop) {
|
|
125
|
-
if (prop === PROXY_TARGET_SYMBOL) {
|
|
126
|
-
return fnTarget;
|
|
127
|
-
}
|
|
128
|
-
if (typeof prop === "string" &&
|
|
129
|
-
BLOCKED_SANDBOX_PROPERTIES.has(prop)) {
|
|
130
|
-
return undefined;
|
|
131
|
-
}
|
|
132
|
-
const val = Reflect.get(fnTarget, prop, fnTarget);
|
|
133
|
-
return createSandboxProxy(val, cache, fnTarget);
|
|
134
|
-
},
|
|
135
|
-
getPrototypeOf() {
|
|
136
|
-
return null;
|
|
137
|
-
},
|
|
138
|
-
apply(fnTarget, _thisArg, args) {
|
|
139
|
-
const thisObj = (parentObj ||
|
|
140
|
-
fnTarget);
|
|
141
|
-
try {
|
|
142
|
-
const result = Reflect.apply(fnTarget, thisObj, args);
|
|
143
|
-
if (result instanceof Promise) {
|
|
144
|
-
return result.then((v) => {
|
|
145
|
-
return createSandboxProxy(v, cache);
|
|
146
|
-
}, (err) => {
|
|
147
|
-
throw createSandboxProxy(err, cache);
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return createSandboxProxy(result, cache);
|
|
151
|
-
}
|
|
152
|
-
catch (err) {
|
|
153
|
-
throw createSandboxProxy(err, cache);
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
has(fnTarget, prop) {
|
|
157
|
-
if (typeof prop === "string" &&
|
|
158
|
-
BLOCKED_SANDBOX_PROPERTIES.has(prop)) {
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
return Reflect.has(fnTarget, prop);
|
|
162
|
-
},
|
|
163
|
-
ownKeys(fnTarget) {
|
|
164
|
-
return Reflect.ownKeys(fnTarget).filter((k) => {
|
|
165
|
-
return !(typeof k === "string" && BLOCKED_SANDBOX_PROPERTIES.has(k));
|
|
166
|
-
});
|
|
167
|
-
},
|
|
168
|
-
getOwnPropertyDescriptor(fnTarget, prop) {
|
|
169
|
-
if (typeof prop === "string" &&
|
|
170
|
-
BLOCKED_SANDBOX_PROPERTIES.has(prop)) {
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
const desc = Reflect.getOwnPropertyDescriptor(fnTarget, prop);
|
|
174
|
-
if (desc && "value" in desc) {
|
|
175
|
-
desc.value = createSandboxProxy(desc.value, cache, fnTarget);
|
|
176
|
-
}
|
|
177
|
-
return desc;
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
return fnProxy;
|
|
181
|
-
}
|
|
182
|
-
// Object — use cache to preserve identity and handle circular references
|
|
183
|
-
if (cache.has(target)) {
|
|
184
|
-
return cache.get(target);
|
|
185
|
-
}
|
|
186
|
-
const objProxy = new Proxy(target, {
|
|
187
|
-
get(objTarget, prop) {
|
|
188
|
-
if (prop === PROXY_TARGET_SYMBOL) {
|
|
189
|
-
return objTarget;
|
|
190
|
-
}
|
|
191
|
-
if (typeof prop === "string" && BLOCKED_SANDBOX_PROPERTIES.has(prop)) {
|
|
192
|
-
return undefined;
|
|
193
|
-
}
|
|
194
|
-
const val = Reflect.get(objTarget, prop, objTarget);
|
|
195
|
-
return createSandboxProxy(val, cache, objTarget);
|
|
196
|
-
},
|
|
197
|
-
getPrototypeOf() {
|
|
198
|
-
return null;
|
|
199
|
-
},
|
|
200
|
-
set(objTarget, prop, newValue) {
|
|
201
|
-
return Reflect.set(objTarget, prop, newValue);
|
|
202
|
-
},
|
|
203
|
-
has(objTarget, prop) {
|
|
204
|
-
if (typeof prop === "string" && BLOCKED_SANDBOX_PROPERTIES.has(prop)) {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
return Reflect.has(objTarget, prop);
|
|
208
|
-
},
|
|
209
|
-
ownKeys(objTarget) {
|
|
210
|
-
return Reflect.ownKeys(objTarget).filter((k) => {
|
|
211
|
-
return !(typeof k === "string" && BLOCKED_SANDBOX_PROPERTIES.has(k));
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
getOwnPropertyDescriptor(objTarget, prop) {
|
|
215
|
-
if (typeof prop === "string" && BLOCKED_SANDBOX_PROPERTIES.has(prop)) {
|
|
216
|
-
return undefined;
|
|
217
|
-
}
|
|
218
|
-
const desc = Reflect.getOwnPropertyDescriptor(objTarget, prop);
|
|
219
|
-
if (desc && "value" in desc) {
|
|
220
|
-
desc.value = createSandboxProxy(desc.value, cache, objTarget);
|
|
221
|
-
}
|
|
222
|
-
return desc;
|
|
223
|
-
},
|
|
224
|
-
});
|
|
225
|
-
cache.set(target, objProxy);
|
|
226
|
-
return objProxy;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Recursively unwraps sandbox proxies in a return value so the host code
|
|
230
|
-
* receives original objects (e.g. Buffers that pass `instanceof` checks).
|
|
231
|
-
*/
|
|
232
|
-
export function deepUnwrapProxies(value, visited) {
|
|
233
|
-
if (value === null || value === undefined) {
|
|
234
|
-
return value;
|
|
235
|
-
}
|
|
236
|
-
const valueType = typeof value;
|
|
237
|
-
if (valueType !== "object" && valueType !== "function") {
|
|
238
|
-
return value;
|
|
239
|
-
}
|
|
240
|
-
const obj = value;
|
|
241
|
-
// If it's one of our proxies, unwrap to the original target
|
|
242
|
-
try {
|
|
243
|
-
const underlying = obj[PROXY_TARGET_SYMBOL];
|
|
244
|
-
if (underlying !== undefined) {
|
|
245
|
-
return underlying;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
catch (_a) {
|
|
249
|
-
// Not a proxy or symbol access failed — treat as a plain value
|
|
250
|
-
}
|
|
251
|
-
if (!visited) {
|
|
252
|
-
visited = new WeakSet();
|
|
253
|
-
}
|
|
254
|
-
if (visited.has(obj)) {
|
|
255
|
-
return obj;
|
|
256
|
-
}
|
|
257
|
-
visited.add(obj);
|
|
258
|
-
if (Array.isArray(obj)) {
|
|
259
|
-
for (let i = 0; i < obj.length; i++) {
|
|
260
|
-
obj[i] = deepUnwrapProxies(obj[i], visited);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
else if (valueType === "object") {
|
|
264
|
-
for (const key of Object.keys(obj)) {
|
|
265
|
-
obj[key] = deepUnwrapProxies(obj[key], visited);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return obj;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Unwraps a single value if it is a sandbox proxy, otherwise returns it as-is.
|
|
272
|
-
*/
|
|
273
|
-
function unwrapProxy(value) {
|
|
274
|
-
if (value && typeof value === "object") {
|
|
275
|
-
const underlying = value[PROXY_TARGET_SYMBOL];
|
|
276
|
-
if (underlying !== undefined) {
|
|
277
|
-
return underlying;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return value;
|
|
281
|
-
}
|
|
282
17
|
export default class VMRunner {
|
|
283
18
|
/*
|
|
284
19
|
* Works out which URL the sandbox's axios call will actually dial, so the
|
|
@@ -313,188 +48,123 @@ export default class VMRunner {
|
|
|
313
48
|
}
|
|
314
49
|
return absolute;
|
|
315
50
|
}
|
|
316
|
-
static async
|
|
51
|
+
static async runCodeInSandbox(data) {
|
|
317
52
|
const { code, options } = data;
|
|
318
53
|
const timeout = options.timeout || 5000;
|
|
319
54
|
const logMessages = [];
|
|
320
|
-
const MAX_LOG_BYTES = 1000000; // 1MB cap
|
|
321
|
-
let totalLogBytes = 0;
|
|
322
55
|
const capturedMetrics = [];
|
|
323
56
|
const MAX_METRICS = 100;
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const handle = setInterval(fn, ms, ...rest);
|
|
341
|
-
pendingIntervals.push(handle);
|
|
342
|
-
return handle;
|
|
343
|
-
};
|
|
344
|
-
const wrappedClearInterval = (handle) => {
|
|
345
|
-
const actual = unwrapProxy(handle);
|
|
346
|
-
clearInterval(actual);
|
|
347
|
-
const idx = pendingIntervals.indexOf(actual);
|
|
348
|
-
if (idx !== -1) {
|
|
349
|
-
pendingIntervals.splice(idx, 1);
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
// Proxy cache shared across all wrapped host objects in this execution
|
|
353
|
-
const proxyCache = new WeakMap();
|
|
354
|
-
// Use null-prototype object to break this.constructor chain on the global
|
|
355
|
-
const sandbox = Object.create(null);
|
|
356
|
-
sandbox["process"] = Object.freeze(Object.create(null));
|
|
357
|
-
sandbox["console"] = createSandboxProxy({
|
|
358
|
-
log: (...args) => {
|
|
359
|
-
const msg = args.join(" ");
|
|
360
|
-
totalLogBytes += msg.length;
|
|
361
|
-
if (totalLogBytes <= MAX_LOG_BYTES) {
|
|
362
|
-
logMessages.push(msg);
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
}, proxyCache);
|
|
366
|
-
sandbox["http"] = createSandboxProxy(http, proxyCache);
|
|
367
|
-
sandbox["https"] = createSandboxProxy(https, proxyCache);
|
|
368
|
-
sandbox["axios"] = createSandboxProxy(axios, proxyCache);
|
|
369
|
-
sandbox["crypto"] = createSandboxProxy(crypto, proxyCache);
|
|
370
|
-
sandbox["setTimeout"] = createSandboxProxy(wrappedSetTimeout, proxyCache);
|
|
371
|
-
sandbox["clearTimeout"] = createSandboxProxy(wrappedClearTimeout, proxyCache);
|
|
372
|
-
sandbox["setInterval"] = createSandboxProxy(wrappedSetInterval, proxyCache);
|
|
373
|
-
sandbox["clearInterval"] = createSandboxProxy(wrappedClearInterval, proxyCache);
|
|
374
|
-
sandbox["oneuptime"] = createSandboxProxy({
|
|
375
|
-
captureMetric: (name, value, attributes) => {
|
|
376
|
-
if (typeof name !== "string" || name.length === 0) {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
if (typeof value !== "number" || isNaN(value)) {
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
if (capturedMetrics.length >= MAX_METRICS) {
|
|
383
|
-
return;
|
|
57
|
+
const MAX_LOG_MESSAGES = 1000;
|
|
58
|
+
const MAX_LOG_BYTES = 1000000;
|
|
59
|
+
const MAX_SCRIPT_ERROR_MESSAGE_LENGTH = 10000;
|
|
60
|
+
let logBytes = 0;
|
|
61
|
+
const pendingHostTimeouts = new Set();
|
|
62
|
+
const pendingAxiosControllers = new Set();
|
|
63
|
+
let acceptingHostOperations = true;
|
|
64
|
+
const pendingAxiosOperations = new Map();
|
|
65
|
+
let nextAxiosOperationId = 0;
|
|
66
|
+
const pendingSleepOperations = new Map();
|
|
67
|
+
let nextSleepOperationId = 0;
|
|
68
|
+
const sanitizeScriptError = (error) => {
|
|
69
|
+
let message = "Sandbox script failed";
|
|
70
|
+
try {
|
|
71
|
+
if (typeof error === "string") {
|
|
72
|
+
message = error;
|
|
384
73
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (attributes && typeof attributes === "object") {
|
|
390
|
-
const safeAttrs = {};
|
|
391
|
-
for (const [k, v] of Object.entries(attributes)) {
|
|
392
|
-
if (typeof v === "string" ||
|
|
393
|
-
typeof v === "number" ||
|
|
394
|
-
typeof v === "boolean") {
|
|
395
|
-
safeAttrs[k] = String(v);
|
|
396
|
-
}
|
|
74
|
+
else if (error && typeof error === "object") {
|
|
75
|
+
const candidateMessage = error["message"];
|
|
76
|
+
if (typeof candidateMessage === "string") {
|
|
77
|
+
message = candidateMessage;
|
|
397
78
|
}
|
|
398
|
-
metric.attributes = safeAttrs;
|
|
399
79
|
}
|
|
400
|
-
capturedMetrics.push(metric);
|
|
401
|
-
},
|
|
402
|
-
}, proxyCache);
|
|
403
|
-
// Wrap any additional context (e.g. Playwright browser/page objects)
|
|
404
|
-
if (options.context) {
|
|
405
|
-
for (const key of Object.keys(options.context)) {
|
|
406
|
-
const val = options.context[key];
|
|
407
|
-
sandbox[key] =
|
|
408
|
-
typeof val === "string" ? val : createSandboxProxy(val, proxyCache);
|
|
409
80
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
// args is plain JSON data — no host functions to protect against
|
|
413
|
-
sandbox["args"] = options.args;
|
|
414
|
-
}
|
|
415
|
-
vm.createContext(sandbox, {
|
|
416
|
-
codeGeneration: {
|
|
417
|
-
strings: false,
|
|
418
|
-
wasm: false,
|
|
419
|
-
},
|
|
420
|
-
});
|
|
421
|
-
const script = `(async()=>{
|
|
422
|
-
${VM_HARDENING_PRELUDE}
|
|
423
|
-
${code}
|
|
424
|
-
})()`;
|
|
425
|
-
try {
|
|
426
|
-
let returnVal;
|
|
427
|
-
let scriptError;
|
|
428
|
-
try {
|
|
429
|
-
/*
|
|
430
|
-
* vm timeout only covers synchronous CPU time, so wrap with
|
|
431
|
-
* Promise.race to also cover async operations (network, timers, etc.)
|
|
432
|
-
*/
|
|
433
|
-
const vmPromise = vm.runInContext(script, sandbox, {
|
|
434
|
-
timeout: timeout,
|
|
435
|
-
});
|
|
436
|
-
const overallTimeout = new Promise((_resolve, reject) => {
|
|
437
|
-
const handle = global.setTimeout(() => {
|
|
438
|
-
reject(new Error("Script execution timed out"));
|
|
439
|
-
}, timeout + 5000);
|
|
440
|
-
// Don't let this timer keep the process alive
|
|
441
|
-
handle.unref();
|
|
442
|
-
});
|
|
443
|
-
returnVal = await Promise.race([vmPromise, overallTimeout]);
|
|
81
|
+
catch (_a) {
|
|
82
|
+
// Do not invoke any attacker-controlled coercion while reporting errors.
|
|
444
83
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
returnValue: deepUnwrapProxies(returnVal),
|
|
459
|
-
logMessages,
|
|
460
|
-
capturedMetrics,
|
|
461
|
-
scriptError,
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
finally {
|
|
465
|
-
// Clean up any lingering timers to prevent resource leaks
|
|
466
|
-
for (const handle of pendingTimeouts) {
|
|
467
|
-
clearTimeout(handle);
|
|
84
|
+
let sanitizedMessage = "";
|
|
85
|
+
for (const character of message) {
|
|
86
|
+
const characterCode = character.charCodeAt(0);
|
|
87
|
+
if (characterCode === 9 ||
|
|
88
|
+
characterCode === 10 ||
|
|
89
|
+
characterCode === 13 ||
|
|
90
|
+
(characterCode >= 32 && characterCode !== 127)) {
|
|
91
|
+
sanitizedMessage += character;
|
|
92
|
+
}
|
|
93
|
+
if (sanitizedMessage.length >= MAX_SCRIPT_ERROR_MESSAGE_LENGTH) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
468
96
|
}
|
|
469
|
-
|
|
470
|
-
|
|
97
|
+
message = sanitizedMessage.substring(0, MAX_SCRIPT_ERROR_MESSAGE_LENGTH);
|
|
98
|
+
if (!message) {
|
|
99
|
+
message = "Sandbox script failed";
|
|
471
100
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const capturedMetrics = [];
|
|
479
|
-
const MAX_METRICS = 100;
|
|
101
|
+
/*
|
|
102
|
+
* Deliberately create a fresh host Error so isolate-owned properties and
|
|
103
|
+
* stack frames never escape with the result.
|
|
104
|
+
*/
|
|
105
|
+
return new Error(message);
|
|
106
|
+
};
|
|
480
107
|
const isolate = new ivm.Isolate({ memoryLimit: 128 });
|
|
481
108
|
try {
|
|
482
109
|
const context = await isolate.createContext();
|
|
483
110
|
const jail = context.global;
|
|
484
111
|
// Set up global object
|
|
485
112
|
await jail.set("global", jail.derefInto());
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
113
|
+
/*
|
|
114
|
+
* Callback values become ordinary functions in the destination isolate.
|
|
115
|
+
* Never expose ivm.Reference or ivm.ExternalCopy handles to user code:
|
|
116
|
+
* their prototype methods can be used to cross the isolate boundary.
|
|
117
|
+
*/
|
|
118
|
+
await jail.set("__oneuptimeHostLogCallback", new ivm.Callback((message) => {
|
|
119
|
+
if (logMessages.length >= MAX_LOG_MESSAGES) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const messageBytes = Buffer.byteLength(message, "utf8");
|
|
123
|
+
if (logBytes + messageBytes > MAX_LOG_BYTES) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
logBytes += messageBytes;
|
|
127
|
+
logMessages.push(message);
|
|
128
|
+
}, { sync: true }));
|
|
490
129
|
await context.eval(`
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
130
|
+
(() => {
|
|
131
|
+
const hostLog = globalThis.__oneuptimeHostLogCallback;
|
|
132
|
+
delete globalThis.__oneuptimeHostLogCallback;
|
|
133
|
+
let sandboxLogCount = 0;
|
|
134
|
+
let sandboxLogCharacters = 0;
|
|
135
|
+
|
|
136
|
+
const sandboxConsole = Object.freeze({
|
|
137
|
+
log: (...args) => {
|
|
138
|
+
if (sandboxLogCount >= 1000 || sandboxLogCharacters >= 500000) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const message = args.map(value => {
|
|
143
|
+
try {
|
|
144
|
+
return typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
145
|
+
} catch (_) {
|
|
146
|
+
return String(value);
|
|
147
|
+
}
|
|
148
|
+
}).join(' ').substring(0, 250000);
|
|
149
|
+
|
|
150
|
+
if (sandboxLogCharacters + message.length > 500000) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
sandboxLogCount += 1;
|
|
155
|
+
sandboxLogCharacters += message.length;
|
|
156
|
+
hostLog(message);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
Object.defineProperty(globalThis, 'console', {
|
|
161
|
+
value: sandboxConsole,
|
|
162
|
+
writable: false,
|
|
163
|
+
configurable: false,
|
|
164
|
+
});
|
|
165
|
+
})();
|
|
495
166
|
`);
|
|
496
|
-
|
|
497
|
-
await jail.set("_captureMetric", new ivm.Callback((name, value, attributesJson) => {
|
|
167
|
+
await jail.set("__oneuptimeHostMetricCallback", new ivm.Callback((name, value, attributesJson) => {
|
|
498
168
|
if (capturedMetrics.length >= MAX_METRICS) {
|
|
499
169
|
return;
|
|
500
170
|
}
|
|
@@ -515,25 +185,43 @@ export default class VMRunner {
|
|
|
515
185
|
}
|
|
516
186
|
}
|
|
517
187
|
capturedMetrics.push(metric);
|
|
518
|
-
}));
|
|
188
|
+
}, { sync: true }));
|
|
519
189
|
await context.eval(`
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
190
|
+
(() => {
|
|
191
|
+
const hostCaptureMetric = globalThis.__oneuptimeHostMetricCallback;
|
|
192
|
+
delete globalThis.__oneuptimeHostMetricCallback;
|
|
193
|
+
|
|
194
|
+
const sandboxOneUptime = Object.freeze({
|
|
195
|
+
captureMetric: (name, value, attributes) => {
|
|
196
|
+
if (typeof name !== 'string' || name.length === 0) return;
|
|
197
|
+
if (typeof value !== 'number' || isNaN(value)) return;
|
|
198
|
+
const attrJson = attributes ? JSON.stringify(attributes) : undefined;
|
|
199
|
+
hostCaptureMetric(String(name), String(value), attrJson);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
Object.defineProperty(globalThis, 'oneuptime', {
|
|
204
|
+
value: sandboxOneUptime,
|
|
205
|
+
writable: false,
|
|
206
|
+
configurable: false,
|
|
207
|
+
});
|
|
208
|
+
})();
|
|
528
209
|
`);
|
|
529
210
|
// args - deep copy into isolate
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
211
|
+
await jail.set("__oneuptimeCopiedArgs", options.args || {}, {
|
|
212
|
+
copy: true,
|
|
213
|
+
});
|
|
214
|
+
await context.eval(`
|
|
215
|
+
(() => {
|
|
216
|
+
const copiedArgs = globalThis.__oneuptimeCopiedArgs;
|
|
217
|
+
delete globalThis.__oneuptimeCopiedArgs;
|
|
218
|
+
Object.defineProperty(globalThis, 'args', {
|
|
219
|
+
value: copiedArgs,
|
|
220
|
+
writable: false,
|
|
221
|
+
configurable: false,
|
|
222
|
+
});
|
|
223
|
+
})();
|
|
224
|
+
`);
|
|
537
225
|
/*
|
|
538
226
|
* http / https - provide Agent constructors that serialize across the boundary.
|
|
539
227
|
* The sandbox Agent is a plain object with a marker; the host-side axios bridge
|
|
@@ -559,13 +247,13 @@ export default class VMRunner {
|
|
|
559
247
|
`);
|
|
560
248
|
/*
|
|
561
249
|
* axios (get, head, options, post, put, patch, delete, request)
|
|
562
|
-
* bridged
|
|
250
|
+
* bridged through a copied async callback.
|
|
563
251
|
*
|
|
564
252
|
* For GET/HEAD/OPTIONS/DELETE: args = [method, url, configJson?]
|
|
565
253
|
* For POST/PUT/PATCH: args = [method, url, bodyJson?, configJson?]
|
|
566
254
|
* For REQUEST: args = ['request', '', configJson]
|
|
567
255
|
*/
|
|
568
|
-
const
|
|
256
|
+
const executeAxiosRequest = async (signal, method, url, arg1, arg2) => {
|
|
569
257
|
const methodsWithBody = ["post", "put", "patch"];
|
|
570
258
|
const hasBody = methodsWithBody.includes(method);
|
|
571
259
|
/*
|
|
@@ -640,6 +328,11 @@ export default class VMRunner {
|
|
|
640
328
|
delete safeConfig["transport"];
|
|
641
329
|
delete safeConfig["adapter"];
|
|
642
330
|
safeConfig["maxRedirects"] = 0;
|
|
331
|
+
Object.defineProperty(safeConfig, "signal", {
|
|
332
|
+
value: signal,
|
|
333
|
+
enumerable: true,
|
|
334
|
+
configurable: true,
|
|
335
|
+
});
|
|
643
336
|
config = safeConfig;
|
|
644
337
|
try {
|
|
645
338
|
let response;
|
|
@@ -702,10 +395,86 @@ export default class VMRunner {
|
|
|
702
395
|
}
|
|
703
396
|
throw err;
|
|
704
397
|
}
|
|
705
|
-
}
|
|
706
|
-
|
|
398
|
+
};
|
|
399
|
+
const axiosStartCallback = new ivm.Callback((method, url, arg1, arg2) => {
|
|
400
|
+
const operationId = String(++nextAxiosOperationId);
|
|
401
|
+
if (!acceptingHostOperations) {
|
|
402
|
+
return operationId;
|
|
403
|
+
}
|
|
404
|
+
if (pendingAxiosOperations.size >= 100) {
|
|
405
|
+
pendingAxiosOperations.set(operationId, {
|
|
406
|
+
status: "rejected",
|
|
407
|
+
errorMessage: "Too many pending HTTP requests",
|
|
408
|
+
});
|
|
409
|
+
return operationId;
|
|
410
|
+
}
|
|
411
|
+
pendingAxiosOperations.set(operationId, { status: "pending" });
|
|
412
|
+
const abortController = new AbortController();
|
|
413
|
+
pendingAxiosControllers.add(abortController);
|
|
414
|
+
void executeAxiosRequest(abortController.signal, method, url, arg1, arg2)
|
|
415
|
+
.then((value) => {
|
|
416
|
+
if (pendingAxiosOperations.has(operationId)) {
|
|
417
|
+
pendingAxiosOperations.set(operationId, {
|
|
418
|
+
status: "fulfilled",
|
|
419
|
+
value,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}, (error) => {
|
|
423
|
+
if (pendingAxiosOperations.has(operationId)) {
|
|
424
|
+
pendingAxiosOperations.set(operationId, {
|
|
425
|
+
status: "rejected",
|
|
426
|
+
errorMessage: sanitizeScriptError(error).message,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
})
|
|
430
|
+
.finally(() => {
|
|
431
|
+
pendingAxiosControllers.delete(abortController);
|
|
432
|
+
});
|
|
433
|
+
return operationId;
|
|
434
|
+
}, { async: true });
|
|
435
|
+
const axiosPollCallback = new ivm.Callback((operationId) => {
|
|
436
|
+
const operation = pendingAxiosOperations.get(operationId);
|
|
437
|
+
if (!operation) {
|
|
438
|
+
return JSON.stringify({
|
|
439
|
+
status: "rejected",
|
|
440
|
+
errorMessage: "HTTP request result is unavailable",
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
if (operation.status !== "pending") {
|
|
444
|
+
pendingAxiosOperations.delete(operationId);
|
|
445
|
+
}
|
|
446
|
+
return JSON.stringify(operation);
|
|
447
|
+
}, { async: true });
|
|
448
|
+
await jail.set("__oneuptimeHostAxiosStartCallback", axiosStartCallback);
|
|
449
|
+
await jail.set("__oneuptimeHostAxiosPollCallback", axiosPollCallback);
|
|
707
450
|
await context.eval(`
|
|
708
|
-
|
|
451
|
+
(() => {
|
|
452
|
+
const hostAxiosStart = globalThis.__oneuptimeHostAxiosStartCallback;
|
|
453
|
+
const hostAxiosPoll = globalThis.__oneuptimeHostAxiosPollCallback;
|
|
454
|
+
delete globalThis.__oneuptimeHostAxiosStartCallback;
|
|
455
|
+
delete globalThis.__oneuptimeHostAxiosPollCallback;
|
|
456
|
+
const axiosPollWaitArray = new Int32Array(new SharedArrayBuffer(4));
|
|
457
|
+
|
|
458
|
+
async function hostAxios(method, url, arg1, arg2) {
|
|
459
|
+
const operationId = await hostAxiosStart(method, url, arg1, arg2);
|
|
460
|
+
|
|
461
|
+
while (true) {
|
|
462
|
+
const operation = JSON.parse(await hostAxiosPoll(operationId));
|
|
463
|
+
|
|
464
|
+
if (operation.status === 'pending') {
|
|
465
|
+
Atomics.wait(axiosPollWaitArray, 0, 0, 1);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (operation.status === 'rejected') {
|
|
470
|
+
throw new Error(operation.errorMessage);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return operation.value;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function assertNoFunctions(obj, path) {
|
|
709
478
|
if (!obj || typeof obj !== 'object') return;
|
|
710
479
|
if (Array.isArray(obj)) {
|
|
711
480
|
for (let i = 0; i < obj.length; i++) {
|
|
@@ -717,7 +486,7 @@ export default class VMRunner {
|
|
|
717
486
|
);
|
|
718
487
|
}
|
|
719
488
|
if (obj[i] && typeof obj[i] === 'object') {
|
|
720
|
-
|
|
489
|
+
assertNoFunctions(obj[i], fullPath);
|
|
721
490
|
}
|
|
722
491
|
}
|
|
723
492
|
return;
|
|
@@ -731,12 +500,12 @@ export default class VMRunner {
|
|
|
731
500
|
);
|
|
732
501
|
}
|
|
733
502
|
if (obj[key] && typeof obj[key] === 'object') {
|
|
734
|
-
|
|
503
|
+
assertNoFunctions(obj[key], fullPath);
|
|
735
504
|
}
|
|
736
505
|
}
|
|
737
506
|
}
|
|
738
507
|
|
|
739
|
-
function
|
|
508
|
+
function parseAxiosResult(r) {
|
|
740
509
|
const parsed = JSON.parse(r);
|
|
741
510
|
if (parsed && parsed.__isAxiosError) {
|
|
742
511
|
const err = new Error(parsed.message);
|
|
@@ -753,7 +522,7 @@ export default class VMRunner {
|
|
|
753
522
|
return parsed;
|
|
754
523
|
}
|
|
755
524
|
|
|
756
|
-
function
|
|
525
|
+
function makeAxiosInstance(defaults) {
|
|
757
526
|
function mergeConfig(overrides) {
|
|
758
527
|
if (!defaults && !overrides) return undefined;
|
|
759
528
|
if (!defaults) return overrides;
|
|
@@ -767,9 +536,9 @@ export default class VMRunner {
|
|
|
767
536
|
|
|
768
537
|
async function _request(config) {
|
|
769
538
|
const merged = mergeConfig(config);
|
|
770
|
-
if (merged)
|
|
771
|
-
const r = await
|
|
772
|
-
return
|
|
539
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
540
|
+
const r = await hostAxios('request', '', merged ? JSON.stringify(merged) : undefined);
|
|
541
|
+
return parseAxiosResult(r);
|
|
773
542
|
}
|
|
774
543
|
|
|
775
544
|
// Make instance callable: axios(config) or axios(url, config)
|
|
@@ -783,62 +552,67 @@ export default class VMRunner {
|
|
|
783
552
|
instance.request = _request;
|
|
784
553
|
instance.get = async (url, config) => {
|
|
785
554
|
const merged = mergeConfig(config);
|
|
786
|
-
if (merged)
|
|
787
|
-
const r = await
|
|
788
|
-
return
|
|
555
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
556
|
+
const r = await hostAxios('get', url, merged ? JSON.stringify(merged) : undefined);
|
|
557
|
+
return parseAxiosResult(r);
|
|
789
558
|
};
|
|
790
559
|
instance.head = async (url, config) => {
|
|
791
560
|
const merged = mergeConfig(config);
|
|
792
|
-
if (merged)
|
|
793
|
-
const r = await
|
|
794
|
-
return
|
|
561
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
562
|
+
const r = await hostAxios('head', url, merged ? JSON.stringify(merged) : undefined);
|
|
563
|
+
return parseAxiosResult(r);
|
|
795
564
|
};
|
|
796
565
|
instance.options = async (url, config) => {
|
|
797
566
|
const merged = mergeConfig(config);
|
|
798
|
-
if (merged)
|
|
799
|
-
const r = await
|
|
800
|
-
return
|
|
567
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
568
|
+
const r = await hostAxios('options', url, merged ? JSON.stringify(merged) : undefined);
|
|
569
|
+
return parseAxiosResult(r);
|
|
801
570
|
};
|
|
802
571
|
instance.post = async (url, data, config) => {
|
|
803
572
|
const merged = mergeConfig(config);
|
|
804
|
-
if (data)
|
|
805
|
-
if (merged)
|
|
806
|
-
const r = await
|
|
807
|
-
return
|
|
573
|
+
if (data) assertNoFunctions(data, 'data');
|
|
574
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
575
|
+
const r = await hostAxios('post', url, data ? JSON.stringify(data) : undefined, merged ? JSON.stringify(merged) : undefined);
|
|
576
|
+
return parseAxiosResult(r);
|
|
808
577
|
};
|
|
809
578
|
instance.put = async (url, data, config) => {
|
|
810
579
|
const merged = mergeConfig(config);
|
|
811
|
-
if (data)
|
|
812
|
-
if (merged)
|
|
813
|
-
const r = await
|
|
814
|
-
return
|
|
580
|
+
if (data) assertNoFunctions(data, 'data');
|
|
581
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
582
|
+
const r = await hostAxios('put', url, data ? JSON.stringify(data) : undefined, merged ? JSON.stringify(merged) : undefined);
|
|
583
|
+
return parseAxiosResult(r);
|
|
815
584
|
};
|
|
816
585
|
instance.patch = async (url, data, config) => {
|
|
817
586
|
const merged = mergeConfig(config);
|
|
818
|
-
if (data)
|
|
819
|
-
if (merged)
|
|
820
|
-
const r = await
|
|
821
|
-
return
|
|
587
|
+
if (data) assertNoFunctions(data, 'data');
|
|
588
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
589
|
+
const r = await hostAxios('patch', url, data ? JSON.stringify(data) : undefined, merged ? JSON.stringify(merged) : undefined);
|
|
590
|
+
return parseAxiosResult(r);
|
|
822
591
|
};
|
|
823
592
|
instance.delete = async (url, config) => {
|
|
824
593
|
const merged = mergeConfig(config);
|
|
825
|
-
if (merged)
|
|
826
|
-
const r = await
|
|
827
|
-
return
|
|
594
|
+
if (merged) assertNoFunctions(merged, 'config');
|
|
595
|
+
const r = await hostAxios('delete', url, merged ? JSON.stringify(merged) : undefined);
|
|
596
|
+
return parseAxiosResult(r);
|
|
828
597
|
};
|
|
829
598
|
instance.create = (instanceDefaults) => {
|
|
830
|
-
if (instanceDefaults)
|
|
599
|
+
if (instanceDefaults) assertNoFunctions(instanceDefaults, 'defaults');
|
|
831
600
|
const combinedDefaults = mergeConfig(instanceDefaults);
|
|
832
|
-
return
|
|
601
|
+
return makeAxiosInstance(combinedDefaults);
|
|
833
602
|
};
|
|
834
603
|
|
|
835
604
|
return instance;
|
|
836
605
|
}
|
|
837
606
|
|
|
838
|
-
|
|
607
|
+
Object.defineProperty(globalThis, 'axios', {
|
|
608
|
+
value: makeAxiosInstance(null),
|
|
609
|
+
writable: false,
|
|
610
|
+
configurable: false,
|
|
611
|
+
});
|
|
612
|
+
})();
|
|
839
613
|
`);
|
|
840
|
-
// crypto (createHash, createHmac, randomBytes, randomUUID, randomInt)
|
|
841
|
-
const
|
|
614
|
+
// crypto (createHash, createHmac, randomBytes, randomUUID, randomInt)
|
|
615
|
+
const cryptoCallback = new ivm.Callback((op, ...args) => {
|
|
842
616
|
switch (op) {
|
|
843
617
|
case "createHash": {
|
|
844
618
|
const [algorithm, inputData, encoding] = args;
|
|
@@ -868,47 +642,138 @@ export default class VMRunner {
|
|
|
868
642
|
default:
|
|
869
643
|
throw new Error(`Unsupported crypto operation: ${op}`);
|
|
870
644
|
}
|
|
871
|
-
});
|
|
872
|
-
await jail.set("
|
|
645
|
+
}, { sync: true });
|
|
646
|
+
await jail.set("__oneuptimeHostCryptoCallback", cryptoCallback);
|
|
873
647
|
await context.eval(`
|
|
874
|
-
|
|
648
|
+
(() => {
|
|
649
|
+
const hostCrypto = globalThis.__oneuptimeHostCryptoCallback;
|
|
650
|
+
delete globalThis.__oneuptimeHostCryptoCallback;
|
|
651
|
+
|
|
652
|
+
const sandboxCrypto = {
|
|
875
653
|
createHash: (algorithm) => ({
|
|
876
654
|
_alg: algorithm, _data: '',
|
|
877
655
|
update(d) { this._data = d; return this; },
|
|
878
|
-
digest(enc) { return
|
|
656
|
+
digest(enc) { return hostCrypto('createHash', this._alg, this._data, enc || 'hex'); }
|
|
879
657
|
}),
|
|
880
658
|
createHmac: (algorithm, key) => ({
|
|
881
659
|
_alg: algorithm, _key: key, _data: '',
|
|
882
660
|
update(d) { this._data = d; return this; },
|
|
883
|
-
digest(enc) { return
|
|
661
|
+
digest(enc) { return hostCrypto('createHmac', this._alg, this._key, this._data, enc || 'hex'); }
|
|
884
662
|
}),
|
|
885
663
|
randomBytes: (size) => ({
|
|
886
|
-
toString(enc) { return
|
|
664
|
+
toString(enc) { return hostCrypto('randomBytes', String(size)); }
|
|
887
665
|
}),
|
|
888
666
|
randomUUID: () => {
|
|
889
|
-
return
|
|
667
|
+
return hostCrypto('randomUUID');
|
|
890
668
|
},
|
|
891
669
|
randomInt: (minOrMax, max) => {
|
|
892
670
|
if (max === undefined) { max = minOrMax; minOrMax = 0; }
|
|
893
|
-
return Number(
|
|
671
|
+
return Number(hostCrypto('randomInt', String(minOrMax), String(max)));
|
|
894
672
|
},
|
|
895
673
|
};
|
|
674
|
+
|
|
675
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
676
|
+
value: sandboxCrypto,
|
|
677
|
+
writable: false,
|
|
678
|
+
configurable: false,
|
|
679
|
+
});
|
|
680
|
+
})();
|
|
896
681
|
`);
|
|
897
|
-
// setTimeout / sleep - bridged
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
682
|
+
// setTimeout / sleep - bridged through copied start/poll callbacks
|
|
683
|
+
const sleepStartCallback = new ivm.Callback((ms) => {
|
|
684
|
+
const operationId = String(++nextSleepOperationId);
|
|
685
|
+
if (!acceptingHostOperations) {
|
|
686
|
+
return operationId;
|
|
687
|
+
}
|
|
688
|
+
const numericDelay = Number(ms);
|
|
689
|
+
const boundedDelay = Number.isFinite(numericDelay)
|
|
690
|
+
? Math.max(0, Math.min(numericDelay, timeout))
|
|
691
|
+
: 0;
|
|
692
|
+
const timeoutHandle = global.setTimeout(() => {
|
|
693
|
+
const operation = pendingSleepOperations.get(operationId);
|
|
694
|
+
if (operation) {
|
|
695
|
+
operation.settled = true;
|
|
696
|
+
}
|
|
697
|
+
pendingHostTimeouts.delete(timeoutHandle);
|
|
698
|
+
}, boundedDelay);
|
|
699
|
+
pendingHostTimeouts.add(timeoutHandle);
|
|
700
|
+
pendingSleepOperations.set(operationId, {
|
|
701
|
+
settled: false,
|
|
901
702
|
});
|
|
902
|
-
|
|
903
|
-
|
|
703
|
+
return operationId;
|
|
704
|
+
}, { async: true });
|
|
705
|
+
const sleepPollCallback = new ivm.Callback((operationId) => {
|
|
706
|
+
const operation = pendingSleepOperations.get(operationId);
|
|
707
|
+
if (!operation || operation.settled) {
|
|
708
|
+
pendingSleepOperations.delete(operationId);
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
return false;
|
|
712
|
+
}, { async: true });
|
|
713
|
+
await jail.set("__oneuptimeHostSleepStartCallback", sleepStartCallback);
|
|
714
|
+
await jail.set("__oneuptimeHostSleepPollCallback", sleepPollCallback);
|
|
904
715
|
await context.eval(`
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
716
|
+
(() => {
|
|
717
|
+
const hostSleepStart = globalThis.__oneuptimeHostSleepStartCallback;
|
|
718
|
+
const hostSleepPoll = globalThis.__oneuptimeHostSleepPollCallback;
|
|
719
|
+
delete globalThis.__oneuptimeHostSleepStartCallback;
|
|
720
|
+
delete globalThis.__oneuptimeHostSleepPollCallback;
|
|
721
|
+
const activeTimers = new WeakSet();
|
|
722
|
+
const pollWaitArray = new Int32Array(new SharedArrayBuffer(4));
|
|
723
|
+
|
|
724
|
+
async function hostSleep(ms) {
|
|
725
|
+
const operationId = await hostSleepStart(ms || 0);
|
|
726
|
+
|
|
727
|
+
while (!(await hostSleepPoll(operationId))) {
|
|
728
|
+
// Pace polling on the isolate worker without blocking Node's
|
|
729
|
+
// event loop.
|
|
730
|
+
Atomics.wait(pollWaitArray, 0, 0, 1);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function sandboxSetTimeout(fn, ms, ...args) {
|
|
735
|
+
if (typeof fn !== 'function') {
|
|
736
|
+
throw new TypeError('setTimeout callback must be a function');
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const handle = {};
|
|
740
|
+
activeTimers.add(handle);
|
|
741
|
+
hostSleep(ms || 0).then(() => {
|
|
742
|
+
if (activeTimers.delete(handle)) {
|
|
743
|
+
fn(...args);
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
return handle;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function sandboxClearTimeout(handle) {
|
|
750
|
+
if (handle && typeof handle === 'object') {
|
|
751
|
+
activeTimers.delete(handle);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
async function sandboxSleep(ms) {
|
|
756
|
+
await hostSleep(ms || 0);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
Object.defineProperties(globalThis, {
|
|
760
|
+
setTimeout: {
|
|
761
|
+
value: sandboxSetTimeout,
|
|
762
|
+
writable: false,
|
|
763
|
+
configurable: false,
|
|
764
|
+
},
|
|
765
|
+
clearTimeout: {
|
|
766
|
+
value: sandboxClearTimeout,
|
|
767
|
+
writable: false,
|
|
768
|
+
configurable: false,
|
|
769
|
+
},
|
|
770
|
+
sleep: {
|
|
771
|
+
value: sandboxSleep,
|
|
772
|
+
writable: false,
|
|
773
|
+
configurable: false,
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
})();
|
|
912
777
|
`);
|
|
913
778
|
/*
|
|
914
779
|
* Wrap user code in async IIFE. JSON.stringify the return value inside
|
|
@@ -923,20 +788,26 @@ export default class VMRunner {
|
|
|
923
788
|
try { return JSON.stringify(__result); }
|
|
924
789
|
catch(_) { return undefined; }
|
|
925
790
|
})()`;
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
timeout
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
791
|
+
let result;
|
|
792
|
+
let scriptError;
|
|
793
|
+
try {
|
|
794
|
+
// Run with overall timeout covering both CPU and I/O wait.
|
|
795
|
+
const resultPromise = context.eval(wrappedCode, {
|
|
796
|
+
promise: true,
|
|
797
|
+
timeout: timeout,
|
|
798
|
+
});
|
|
799
|
+
const overallTimeout = new Promise((_resolve, reject) => {
|
|
800
|
+
const timeoutHandle = global.setTimeout(() => {
|
|
801
|
+
pendingHostTimeouts.delete(timeoutHandle);
|
|
802
|
+
reject(new Error("Script execution timed out"));
|
|
803
|
+
}, timeout + 5000); // 5s grace period beyond isolate timeout
|
|
804
|
+
pendingHostTimeouts.add(timeoutHandle);
|
|
805
|
+
});
|
|
806
|
+
result = await Promise.race([resultPromise, overallTimeout]);
|
|
807
|
+
}
|
|
808
|
+
catch (error) {
|
|
809
|
+
scriptError = sanitizeScriptError(error);
|
|
810
|
+
}
|
|
940
811
|
// Parse the JSON string returned from inside the isolate
|
|
941
812
|
let returnValue;
|
|
942
813
|
if (typeof result === "string") {
|
|
@@ -954,21 +825,27 @@ export default class VMRunner {
|
|
|
954
825
|
returnValue,
|
|
955
826
|
logMessages,
|
|
956
827
|
capturedMetrics,
|
|
828
|
+
scriptError,
|
|
957
829
|
};
|
|
958
830
|
}
|
|
959
831
|
finally {
|
|
832
|
+
acceptingHostOperations = false;
|
|
833
|
+
for (const timeoutHandle of pendingHostTimeouts) {
|
|
834
|
+
global.clearTimeout(timeoutHandle);
|
|
835
|
+
}
|
|
836
|
+
for (const abortController of pendingAxiosControllers) {
|
|
837
|
+
abortController.abort();
|
|
838
|
+
}
|
|
839
|
+
pendingHostTimeouts.clear();
|
|
840
|
+
pendingAxiosControllers.clear();
|
|
841
|
+
pendingSleepOperations.clear();
|
|
842
|
+
pendingAxiosOperations.clear();
|
|
960
843
|
if (!isolate.isDisposed) {
|
|
961
844
|
isolate.dispose();
|
|
962
845
|
}
|
|
963
846
|
}
|
|
964
847
|
}
|
|
965
848
|
}
|
|
966
|
-
__decorate([
|
|
967
|
-
CaptureSpan(),
|
|
968
|
-
__metadata("design:type", Function),
|
|
969
|
-
__metadata("design:paramtypes", [Object]),
|
|
970
|
-
__metadata("design:returntype", Promise)
|
|
971
|
-
], VMRunner, "runCodeInNodeVM", null);
|
|
972
849
|
__decorate([
|
|
973
850
|
CaptureSpan(),
|
|
974
851
|
__metadata("design:type", Function),
|