@neurosec/sentry 1.0.20 → 1.1.0
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/README.md +4 -0
- package/dist/api-auth.d.ts +31 -0
- package/dist/api-auth.d.ts.map +1 -0
- package/dist/api-auth.js +105 -0
- package/dist/api-auth.js.map +1 -0
- package/dist/api-auth.test.d.ts +2 -0
- package/dist/api-auth.test.d.ts.map +1 -0
- package/dist/api-auth.test.js +89 -0
- package/dist/api-auth.test.js.map +1 -0
- package/dist/api.d.ts +8 -7
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +141 -134
- package/dist/api.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +107 -14
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.d.ts +2 -0
- package/dist/cli.test.d.ts.map +1 -0
- package/dist/cli.test.js +68 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +50 -1
- package/dist/config.js.map +1 -1
- package/dist/discovery-win.d.ts +4 -0
- package/dist/discovery-win.d.ts.map +1 -0
- package/dist/discovery-win.js +153 -0
- package/dist/discovery-win.js.map +1 -0
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +23 -97
- package/dist/discovery.js.map +1 -1
- package/dist/discovery.test.js +18 -109
- package/dist/discovery.test.js.map +1 -1
- package/dist/enforcement/file-monitor.d.ts +9 -0
- package/dist/enforcement/file-monitor.d.ts.map +1 -1
- package/dist/enforcement/file-monitor.js +9 -2
- package/dist/enforcement/file-monitor.js.map +1 -1
- package/dist/enforcement/network-monitor.d.ts.map +1 -1
- package/dist/enforcement/network-monitor.js +350 -9
- package/dist/enforcement/network-monitor.js.map +1 -1
- package/dist/enforcement/network-monitor.test.d.ts +2 -0
- package/dist/enforcement/network-monitor.test.d.ts.map +1 -0
- package/dist/enforcement/network-monitor.test.js +52 -0
- package/dist/enforcement/network-monitor.test.js.map +1 -0
- package/dist/enforcement/policy-executor.d.ts +24 -1
- package/dist/enforcement/policy-executor.d.ts.map +1 -1
- package/dist/enforcement/policy-executor.js +213 -69
- package/dist/enforcement/policy-executor.js.map +1 -1
- package/dist/enforcement/policy-executor.test.d.ts +2 -0
- package/dist/enforcement/policy-executor.test.d.ts.map +1 -0
- package/dist/enforcement/policy-executor.test.js +46 -0
- package/dist/enforcement/policy-executor.test.js.map +1 -0
- package/dist/enforcement/target-validator.d.ts +37 -0
- package/dist/enforcement/target-validator.d.ts.map +1 -0
- package/dist/enforcement/target-validator.js +0 -0
- package/dist/enforcement/target-validator.js.map +1 -0
- package/dist/enforcement/target-validator.test.d.ts +2 -0
- package/dist/enforcement/target-validator.test.d.ts.map +1 -0
- package/dist/enforcement/target-validator.test.js +103 -0
- package/dist/enforcement/target-validator.test.js.map +1 -0
- package/dist/http-client.d.ts +35 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +168 -0
- package/dist/http-client.js.map +1 -0
- package/dist/http-client.test.d.ts +2 -0
- package/dist/http-client.test.d.ts.map +1 -0
- package/dist/http-client.test.js +172 -0
- package/dist/http-client.test.js.map +1 -0
- package/dist/index.js +190 -114
- package/dist/index.js.map +1 -1
- package/dist/launcher.d.ts +33 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +425 -0
- package/dist/launcher.js.map +1 -0
- package/dist/launcher.test.d.ts +2 -0
- package/dist/launcher.test.d.ts.map +1 -0
- package/dist/launcher.test.js +109 -0
- package/dist/launcher.test.js.map +1 -0
- package/dist/proxy/cert-manager.d.ts +24 -0
- package/dist/proxy/cert-manager.d.ts.map +1 -0
- package/dist/proxy/cert-manager.js +117 -0
- package/dist/proxy/cert-manager.js.map +1 -0
- package/dist/proxy/cert-manager.test.d.ts +2 -0
- package/dist/proxy/cert-manager.test.d.ts.map +1 -0
- package/dist/proxy/cert-manager.test.js +70 -0
- package/dist/proxy/cert-manager.test.js.map +1 -0
- package/dist/proxy/index.d.ts +61 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +74 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/policy-enforcer.d.ts +30 -0
- package/dist/proxy/policy-enforcer.d.ts.map +1 -0
- package/dist/proxy/policy-enforcer.js +143 -0
- package/dist/proxy/policy-enforcer.js.map +1 -0
- package/dist/proxy/proxy-server.d.ts +42 -0
- package/dist/proxy/proxy-server.d.ts.map +1 -0
- package/dist/proxy/proxy-server.js +652 -0
- package/dist/proxy/proxy-server.js.map +1 -0
- package/dist/proxy/redaction-engine.d.ts +4 -0
- package/dist/proxy/redaction-engine.d.ts.map +1 -0
- package/dist/proxy/redaction-engine.js +50 -0
- package/dist/proxy/redaction-engine.js.map +1 -0
- package/dist/proxy/response-redaction.test.d.ts +2 -0
- package/dist/proxy/response-redaction.test.d.ts.map +1 -0
- package/dist/proxy/response-redaction.test.js +125 -0
- package/dist/proxy/response-redaction.test.js.map +1 -0
- package/dist/proxy/threat-engine.d.ts +22 -0
- package/dist/proxy/threat-engine.d.ts.map +1 -0
- package/dist/proxy/threat-engine.js +291 -0
- package/dist/proxy/threat-engine.js.map +1 -0
- package/dist/proxy/threat-engine.test.d.ts +2 -0
- package/dist/proxy/threat-engine.test.d.ts.map +1 -0
- package/dist/proxy/threat-engine.test.js +27 -0
- package/dist/proxy/threat-engine.test.js.map +1 -0
- package/dist/redirect/env-injector.d.ts +72 -0
- package/dist/redirect/env-injector.d.ts.map +1 -0
- package/dist/redirect/env-injector.js +177 -0
- package/dist/redirect/env-injector.js.map +1 -0
- package/dist/redirect/env-injector.test.d.ts +2 -0
- package/dist/redirect/env-injector.test.d.ts.map +1 -0
- package/dist/redirect/env-injector.test.js +91 -0
- package/dist/redirect/env-injector.test.js.map +1 -0
- package/dist/redirect/index.d.ts +3 -0
- package/dist/redirect/index.d.ts.map +1 -0
- package/dist/redirect/index.js +8 -0
- package/dist/redirect/index.js.map +1 -0
- package/dist/redirect/platform-redirect.d.ts +42 -0
- package/dist/redirect/platform-redirect.d.ts.map +1 -0
- package/dist/redirect/platform-redirect.js +229 -0
- package/dist/redirect/platform-redirect.js.map +1 -0
- package/dist/redirect/platform-redirect.test.d.ts +2 -0
- package/dist/redirect/platform-redirect.test.d.ts.map +1 -0
- package/dist/redirect/platform-redirect.test.js +76 -0
- package/dist/redirect/platform-redirect.test.js.map +1 -0
- package/dist/sandbox/index.d.ts +23 -2
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +24 -7
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/linux-sandbox.d.ts +13 -2
- package/dist/sandbox/linux-sandbox.d.ts.map +1 -1
- package/dist/sandbox/linux-sandbox.js +61 -27
- package/dist/sandbox/linux-sandbox.js.map +1 -1
- package/dist/sandbox/macos-sandbox.d.ts +15 -4
- package/dist/sandbox/macos-sandbox.d.ts.map +1 -1
- package/dist/sandbox/macos-sandbox.js +36 -18
- package/dist/sandbox/macos-sandbox.js.map +1 -1
- package/dist/sandbox/sandbox-result.test.d.ts +2 -0
- package/dist/sandbox/sandbox-result.test.d.ts.map +1 -0
- package/dist/sandbox/sandbox-result.test.js +87 -0
- package/dist/sandbox/sandbox-result.test.js.map +1 -0
- package/dist/sandbox/windows-sandbox.d.ts +34 -0
- package/dist/sandbox/windows-sandbox.d.ts.map +1 -0
- package/dist/sandbox/windows-sandbox.js +161 -0
- package/dist/sandbox/windows-sandbox.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +33 -43
- package/dist/setup.js.map +1 -1
- package/dist/skill-authz/skill-evaluator.d.ts +30 -0
- package/dist/skill-authz/skill-evaluator.d.ts.map +1 -1
- package/dist/skill-authz/skill-evaluator.js +161 -30
- package/dist/skill-authz/skill-evaluator.js.map +1 -1
- package/dist/skill-authz/skill-evaluator.test.d.ts +2 -0
- package/dist/skill-authz/skill-evaluator.test.d.ts.map +1 -0
- package/dist/skill-authz/skill-evaluator.test.js +127 -0
- package/dist/skill-authz/skill-evaluator.test.js.map +1 -0
- package/dist/telemetry.d.ts +2 -8
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +17 -147
- package/dist/telemetry.js.map +1 -1
- package/dist/types.d.ts +48 -105
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +34 -1
- package/dist/types.js.map +1 -1
- package/package.json +7 -3
- package/scripts/install-sentry-windows.ps1 +217 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PlatformRedirect = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const logger_1 = require("../logger");
|
|
10
|
+
/**
|
|
11
|
+
* Platform-specific traffic redirect: divert outbound port-443 from agent
|
|
12
|
+
* processes to the local Sentry HTTPS proxy.
|
|
13
|
+
*
|
|
14
|
+
* Fixes vs prior implementation:
|
|
15
|
+
* - Linux (S-C13): no longer excludes root via `--uid-owner 0`. Many
|
|
16
|
+
* containerized agents run as root; excluding them silently bypassed
|
|
17
|
+
* enforcement. The new rule scopes by destination port + a marked
|
|
18
|
+
* packet match (when available) so root agents are captured but the
|
|
19
|
+
* Sentry daemon's own outbound calls are not (matched by setting a
|
|
20
|
+
* marker UID/GID when daemon socket() runs is out of scope; we instead
|
|
21
|
+
* skip traffic whose dest IP IS the proxy itself).
|
|
22
|
+
* - macOS (S-C14): previous rule used `on lo0` (loopback) which sees no
|
|
23
|
+
* outbound LAN traffic. New rule binds to the primary egress interface
|
|
24
|
+
* resolved at install time via `route -n get default`. Falls back to
|
|
25
|
+
* `pfctl -E` so the redirect anchor stays attached if pf is disabled.
|
|
26
|
+
* - All execs use execFileSync with array args — no shell interpolation.
|
|
27
|
+
*/
|
|
28
|
+
class PlatformRedirect {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
/** Records the exact rule arguments we installed so removeRedirect can undo them. */
|
|
31
|
+
this.installedRules = [];
|
|
32
|
+
this.config = config;
|
|
33
|
+
this.platform = os_1.default.platform();
|
|
34
|
+
}
|
|
35
|
+
async installRedirect() {
|
|
36
|
+
try {
|
|
37
|
+
switch (this.platform) {
|
|
38
|
+
case 'linux': return await this.installLinuxRedirect();
|
|
39
|
+
case 'darwin': return await this.installMacOSRedirect();
|
|
40
|
+
case 'win32': return await this.installWindowsRedirect();
|
|
41
|
+
default:
|
|
42
|
+
logger_1.logger.warn('No redirect support for platform', { platform: this.platform });
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
logger_1.logger.error('Failed to install traffic redirect', {
|
|
48
|
+
platform: this.platform,
|
|
49
|
+
err: err.message,
|
|
50
|
+
});
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async removeRedirect() {
|
|
55
|
+
try {
|
|
56
|
+
switch (this.platform) {
|
|
57
|
+
case 'linux': return await this.removeLinuxRedirect();
|
|
58
|
+
case 'darwin': return await this.removeMacOSRedirect();
|
|
59
|
+
case 'win32': return await this.removeWindowsRedirect();
|
|
60
|
+
default: return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
logger_1.logger.error('Failed to remove traffic redirect', {
|
|
65
|
+
platform: this.platform,
|
|
66
|
+
err: err.message,
|
|
67
|
+
});
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Internal: visible for tests so we can snapshot the exact rule shape. */
|
|
72
|
+
_getInstalledRules() {
|
|
73
|
+
return [...this.installedRules];
|
|
74
|
+
}
|
|
75
|
+
// ── Linux ────────────────────────────────────────────────────────────────
|
|
76
|
+
async installLinuxRedirect() {
|
|
77
|
+
const iptables = this.resolveBin('iptables');
|
|
78
|
+
if (!iptables) {
|
|
79
|
+
logger_1.logger.warn('iptables not available; Linux redirect not installed');
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
const proxyPort = this.config.port;
|
|
83
|
+
// (S-C13) Redirect ALL outbound port-443 (regardless of UID) EXCEPT when
|
|
84
|
+
// destined for the proxy itself (avoids loops with the daemon's own
|
|
85
|
+
// upstream calls). We don't exclude root: containerized agents commonly
|
|
86
|
+
// run as root and were previously silently bypassed.
|
|
87
|
+
const args = [
|
|
88
|
+
'-t', 'nat',
|
|
89
|
+
'-A', 'OUTPUT',
|
|
90
|
+
'-p', 'tcp',
|
|
91
|
+
'--dport', '443',
|
|
92
|
+
'-m', 'addrtype', '!', '--dst-type', 'LOCAL',
|
|
93
|
+
'-j', 'REDIRECT',
|
|
94
|
+
'--to-port', String(proxyPort),
|
|
95
|
+
];
|
|
96
|
+
try {
|
|
97
|
+
(0, child_process_1.execFileSync)(iptables, args, { timeout: 5000, stdio: 'ignore' });
|
|
98
|
+
this.installedRules.push({ kind: 'iptables', args });
|
|
99
|
+
logger_1.logger.info('Linux iptables redirect installed', { proxyPort, args });
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logger_1.logger.warn('iptables rule install failed (non-root?)', { err: err.message });
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async removeLinuxRedirect() {
|
|
108
|
+
const iptables = this.resolveBin('iptables');
|
|
109
|
+
if (!iptables)
|
|
110
|
+
return false;
|
|
111
|
+
for (const rule of this.installedRules) {
|
|
112
|
+
if (rule.kind !== 'iptables')
|
|
113
|
+
continue;
|
|
114
|
+
const deleteArgs = rule.args.map((a) => (a === '-A' ? '-D' : a));
|
|
115
|
+
try {
|
|
116
|
+
(0, child_process_1.execFileSync)(iptables, deleteArgs, { timeout: 5000, stdio: 'ignore' });
|
|
117
|
+
}
|
|
118
|
+
catch { /* best effort */ }
|
|
119
|
+
}
|
|
120
|
+
this.installedRules = this.installedRules.filter((r) => r.kind !== 'iptables');
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
// ── macOS ────────────────────────────────────────────────────────────────
|
|
124
|
+
async installMacOSRedirect() {
|
|
125
|
+
const pfctl = this.resolveBin('pfctl');
|
|
126
|
+
if (!pfctl) {
|
|
127
|
+
logger_1.logger.warn('pfctl not available; macOS redirect not installed');
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
// (S-C14) Resolve the actual egress interface so we don't bind to lo0.
|
|
131
|
+
const iface = this.resolveDefaultInterface();
|
|
132
|
+
if (!iface) {
|
|
133
|
+
logger_1.logger.warn('Could not resolve default egress interface for pf redirect');
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const proxyPort = this.config.port;
|
|
137
|
+
// We do NOT shell out via `bash -c "echo ... > file"` (S-C2 family).
|
|
138
|
+
// Use Node fs to write the anchor file directly.
|
|
139
|
+
const fs = require('fs');
|
|
140
|
+
const path = require('path');
|
|
141
|
+
const anchorPath = '/etc/neuroshield/pf-anchor.conf';
|
|
142
|
+
const rules = `# NeuroShield Sentry — redirect outbound 443 on ${iface} to local proxy\n` +
|
|
143
|
+
`rdr pass on ${iface} inet proto tcp from any to any port 443 -> 127.0.0.1 port ${proxyPort}\n`;
|
|
144
|
+
try {
|
|
145
|
+
fs.mkdirSync(path.dirname(anchorPath), { recursive: true });
|
|
146
|
+
fs.writeFileSync(anchorPath, rules, 'utf8');
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
logger_1.logger.warn('Could not write pf anchor file (needs root)', {
|
|
150
|
+
anchorPath,
|
|
151
|
+
err: err.message,
|
|
152
|
+
});
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
(0, child_process_1.execFileSync)(pfctl, ['-a', 'com.neuroshield/sentry', '-f', anchorPath], { timeout: 5000, stdio: 'ignore' });
|
|
157
|
+
(0, child_process_1.execFileSync)(pfctl, ['-E'], { timeout: 5000, stdio: 'ignore' });
|
|
158
|
+
this.installedRules.push({ kind: 'pf', args: ['-a', 'com.neuroshield/sentry', '-f', anchorPath] });
|
|
159
|
+
logger_1.logger.info('macOS pf redirect installed', { iface, proxyPort, anchorPath });
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
logger_1.logger.warn('macOS pf redirect install failed (needs root)', { err: err.message });
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async removeMacOSRedirect() {
|
|
168
|
+
const pfctl = this.resolveBin('pfctl');
|
|
169
|
+
if (!pfctl)
|
|
170
|
+
return false;
|
|
171
|
+
try {
|
|
172
|
+
(0, child_process_1.execFileSync)(pfctl, ['-a', 'com.neuroshield/sentry', '-F', 'all'], { timeout: 5000, stdio: 'ignore' });
|
|
173
|
+
}
|
|
174
|
+
catch { /* best effort */ }
|
|
175
|
+
this.installedRules = this.installedRules.filter((r) => r.kind !== 'pf');
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
resolveDefaultInterface() {
|
|
179
|
+
try {
|
|
180
|
+
const out = (0, child_process_1.execFileSync)('route', ['-n', 'get', 'default'], { timeout: 2000, encoding: 'utf8' });
|
|
181
|
+
const match = out.match(/interface:\s*(\S+)/);
|
|
182
|
+
return match ? match[1] : null;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// ── Windows ──────────────────────────────────────────────────────────────
|
|
189
|
+
async installWindowsRedirect() {
|
|
190
|
+
const netsh = this.resolveBin('netsh.exe') ?? 'netsh.exe';
|
|
191
|
+
try {
|
|
192
|
+
const proxyAddr = `127.0.0.1:${this.config.port}`;
|
|
193
|
+
(0, child_process_1.execFileSync)(netsh, ['winhttp', 'set', 'proxy', proxyAddr], { timeout: 5000, stdio: 'ignore' });
|
|
194
|
+
this.installedRules.push({ kind: 'winhttp', args: ['winhttp', 'set', 'proxy', proxyAddr] });
|
|
195
|
+
logger_1.logger.info('Windows WinHTTP proxy configured', { proxy: proxyAddr });
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
logger_1.logger.warn('Windows WinHTTP proxy config failed', { err: err.message });
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async removeWindowsRedirect() {
|
|
204
|
+
const netsh = this.resolveBin('netsh.exe') ?? 'netsh.exe';
|
|
205
|
+
try {
|
|
206
|
+
(0, child_process_1.execFileSync)(netsh, ['winhttp', 'reset', 'proxy'], { timeout: 5000, stdio: 'ignore' });
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// ── shared ───────────────────────────────────────────────────────────────
|
|
214
|
+
resolveBin(name) {
|
|
215
|
+
for (const dir of ['/usr/sbin', '/sbin', '/usr/local/sbin', '/usr/bin', '/bin', 'C:\\Windows\\System32']) {
|
|
216
|
+
const candidate = dir + (dir.includes('\\') ? '\\' : '/') + name;
|
|
217
|
+
try {
|
|
218
|
+
(0, child_process_1.execFileSync)(candidate, ['--version'], { stdio: 'ignore', timeout: 2000 });
|
|
219
|
+
return candidate;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
exports.PlatformRedirect = PlatformRedirect;
|
|
229
|
+
//# sourceMappingURL=platform-redirect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-redirect.js","sourceRoot":"","sources":["../../src/redirect/platform-redirect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-redirect.test.d.ts","sourceRoot":"","sources":["../../src/redirect/platform-redirect.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/**
|
|
7
|
+
* Snapshot-style tests for the redirect rule arguments. We can't actually
|
|
8
|
+
* exec iptables / pfctl in a unit test (would require root and a real
|
|
9
|
+
* netfilter / pf), so we assert the SHAPE of the arguments — the audit
|
|
10
|
+
* findings (S-C13: excludes-root, S-C14: lo0-only) are encoded as regressions
|
|
11
|
+
* we'd see in the arg arrays.
|
|
12
|
+
*/
|
|
13
|
+
const vitest_1 = require("vitest");
|
|
14
|
+
const os_1 = __importDefault(require("os"));
|
|
15
|
+
const platform_redirect_1 = require("./platform-redirect");
|
|
16
|
+
function proxyConfig(port = 9081) {
|
|
17
|
+
return {
|
|
18
|
+
enabled: true, port, bindAddress: '127.0.0.1',
|
|
19
|
+
upstreamTimeoutMs: 0, maxBufferSizeMb: 0,
|
|
20
|
+
interceptHttps: false, certPath: '', keyPath: '',
|
|
21
|
+
allowedProviders: ['*'], blockLocalModels: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
(0, vitest_1.describe)('PlatformRedirect rule shapes (S-C13 / S-C14)', () => {
|
|
25
|
+
(0, vitest_1.it)('Linux rule does NOT exclude root', async () => {
|
|
26
|
+
if (os_1.default.platform() !== 'linux')
|
|
27
|
+
return;
|
|
28
|
+
const r = new platform_redirect_1.PlatformRedirect(proxyConfig());
|
|
29
|
+
await r.installRedirect().catch(() => undefined);
|
|
30
|
+
const rules = r._getInstalledRules();
|
|
31
|
+
// We may not have iptables to install; assert only when at least one tried.
|
|
32
|
+
for (const rule of rules) {
|
|
33
|
+
const arg = rule.args.join(' ');
|
|
34
|
+
(0, vitest_1.expect)(arg).not.toMatch(/--uid-owner\s+0/);
|
|
35
|
+
(0, vitest_1.expect)(arg).not.toMatch(/!\s*--uid-owner\s+0/);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.it)('Linux rule destination match excludes local addresses (avoid daemon loop)', () => {
|
|
39
|
+
// We can inspect the SHAPE by calling install and then checking what was
|
|
40
|
+
// recorded. On non-Linux we simply assert that the install path doesn't
|
|
41
|
+
// throw uncaught errors.
|
|
42
|
+
const r = new platform_redirect_1.PlatformRedirect(proxyConfig());
|
|
43
|
+
(0, vitest_1.expect)(() => r.installRedirect().catch(() => undefined)).not.toThrow();
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)('macOS does not bind to lo0 (S-C14)', async () => {
|
|
46
|
+
if (os_1.default.platform() !== 'darwin')
|
|
47
|
+
return;
|
|
48
|
+
const r = new platform_redirect_1.PlatformRedirect(proxyConfig());
|
|
49
|
+
await r.installRedirect().catch(() => undefined);
|
|
50
|
+
const rules = r._getInstalledRules();
|
|
51
|
+
for (const rule of rules) {
|
|
52
|
+
if (rule.kind !== 'pf')
|
|
53
|
+
continue;
|
|
54
|
+
// The anchor file path may include any interface; the resolved interface
|
|
55
|
+
// would be a real outbound iface (en0, en1, etc.) — NEVER lo0.
|
|
56
|
+
const anchorPath = rule.args[rule.args.indexOf('-f') + 1];
|
|
57
|
+
if (anchorPath) {
|
|
58
|
+
try {
|
|
59
|
+
const content = require('fs').readFileSync(anchorPath, 'utf8');
|
|
60
|
+
(0, vitest_1.expect)(content).not.toContain('on lo0 ');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// file may not exist if we lacked permission to write it
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
(0, vitest_1.it)('removeRedirect drops the matching rule from the installed list', async () => {
|
|
69
|
+
const r = new platform_redirect_1.PlatformRedirect(proxyConfig());
|
|
70
|
+
await r.installRedirect().catch(() => undefined);
|
|
71
|
+
await r.removeRedirect().catch(() => undefined);
|
|
72
|
+
// After remove, no rules should remain in the installedRules list
|
|
73
|
+
(0, vitest_1.expect)(r._getInstalledRules()).toHaveLength(0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=platform-redirect.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-redirect.test.js","sourceRoot":"","sources":["../../src/redirect/platform-redirect.test.ts"],"names":[],"mappings":"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"}
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import { TaggedProcess, SandboxProfile } from '../types';
|
|
2
2
|
import { SentryConfig } from '../config';
|
|
3
|
+
/**
|
|
4
|
+
* Structured result returned by every sandbox backend.
|
|
5
|
+
*
|
|
6
|
+
* Honesty contract (replaces the prior boolean return that let backends claim
|
|
7
|
+
* success without applying anything):
|
|
8
|
+
* - `applied` MUST be true only if the kernel actually enforces at least one
|
|
9
|
+
* restriction listed in `features`.
|
|
10
|
+
* - `features` enumerates what was actually enforced (e.g. ['cgroup_cpu',
|
|
11
|
+
* 'cgroup_memory']). Empty array + applied=true is a bug.
|
|
12
|
+
* - `reason` describes WHY enforcement was partial / not applied — surfaced
|
|
13
|
+
* to telemetry so operators see degraded protection in the dashboard
|
|
14
|
+
* instead of a false "sandboxed" badge.
|
|
15
|
+
*
|
|
16
|
+
* Backends that cannot enforce on a running PID (macOS, fork-time-only Linux
|
|
17
|
+
* primitives) MUST return applied=false with a clear reason.
|
|
18
|
+
*/
|
|
19
|
+
export interface SandboxResult {
|
|
20
|
+
applied: boolean;
|
|
21
|
+
features: string[];
|
|
22
|
+
reason?: string;
|
|
23
|
+
}
|
|
3
24
|
export interface SandboxBackend {
|
|
4
|
-
applySandbox(pid: number, profile: SandboxProfile, defaults: SentryConfig['sandboxDefaults']): Promise<
|
|
25
|
+
applySandbox(pid: number, profile: SandboxProfile, defaults: SentryConfig['sandboxDefaults']): Promise<SandboxResult>;
|
|
5
26
|
removeSandbox(pid: number): Promise<boolean>;
|
|
6
27
|
isSandboxed(pid: number): Promise<boolean>;
|
|
7
28
|
getSandboxStats(pid: number): Promise<Record<string, string | number> | null>;
|
|
8
29
|
}
|
|
9
30
|
export declare function getProfileForFramework(frameworkId: string): SandboxProfile | null;
|
|
10
|
-
export declare function applySandbox(process: TaggedProcess, config: SentryConfig): Promise<
|
|
31
|
+
export declare function applySandbox(process: TaggedProcess, config: SentryConfig): Promise<SandboxResult>;
|
|
11
32
|
export declare function removeSandbox(pid: number): Promise<boolean>;
|
|
12
33
|
export declare function isSandboxed(pid: number): Promise<boolean>;
|
|
13
34
|
export declare function getSandboxStats(pid: number): Promise<Record<string, string | number> | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,cAAc,EAA0B,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,cAAc,EAA0B,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAMzC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,CAAC,iBAAiB,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACtH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;CAC/E;AA2BD,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYjF;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAoCvG;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQjE;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE/D;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,CAElG"}
|
package/dist/sandbox/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const types_1 = require("../types");
|
|
|
13
13
|
const logger_1 = require("../logger");
|
|
14
14
|
const linux_sandbox_1 = require("./linux-sandbox");
|
|
15
15
|
const macos_sandbox_1 = require("./macos-sandbox");
|
|
16
|
+
const windows_sandbox_1 = require("./windows-sandbox");
|
|
16
17
|
let backend = null;
|
|
17
18
|
function getBackend() {
|
|
18
19
|
if (backend)
|
|
@@ -24,10 +25,13 @@ function getBackend() {
|
|
|
24
25
|
else if (platform === 'darwin') {
|
|
25
26
|
backend = new macos_sandbox_1.MacOSSandbox();
|
|
26
27
|
}
|
|
28
|
+
else if (platform === 'win32') {
|
|
29
|
+
backend = new windows_sandbox_1.WindowsSandbox();
|
|
30
|
+
}
|
|
27
31
|
else {
|
|
28
32
|
logger_1.logger.warn('No sandbox backend for platform, using no-op', { platform });
|
|
29
33
|
backend = {
|
|
30
|
-
applySandbox: async () => false,
|
|
34
|
+
applySandbox: async () => ({ applied: false, features: [], reason: `platform ${platform} is not supported` }),
|
|
31
35
|
removeSandbox: async () => false,
|
|
32
36
|
isSandboxed: async () => false,
|
|
33
37
|
getSandboxStats: async () => null,
|
|
@@ -49,27 +53,40 @@ function getProfileForFramework(frameworkId) {
|
|
|
49
53
|
return null;
|
|
50
54
|
}
|
|
51
55
|
async function applySandbox(process, config) {
|
|
56
|
+
// Unknown agents now fall through to default-restrictive so they get
|
|
57
|
+
// SOMETHING applied rather than nothing (S-C15).
|
|
52
58
|
const profileName = process.sandboxProfileName ?? 'default-restrictive';
|
|
53
59
|
const profile = types_1.AGENT_SANDBOX_PROFILES.find(p => p.name === profileName)
|
|
54
|
-
??
|
|
60
|
+
?? types_1.AGENT_SANDBOX_PROFILES.find(p => p.name === 'default-restrictive')
|
|
61
|
+
?? null;
|
|
55
62
|
if (!profile) {
|
|
56
63
|
logger_1.logger.warn('No sandbox profile available for process', { pid: process.pid, framework: process.frameworkId });
|
|
57
|
-
return false;
|
|
64
|
+
return { applied: false, features: [], reason: 'no profile available' };
|
|
58
65
|
}
|
|
59
66
|
const bk = getBackend();
|
|
60
67
|
try {
|
|
61
68
|
const result = await bk.applySandbox(process.pid, profile, config.sandboxDefaults);
|
|
62
|
-
if (result) {
|
|
63
|
-
logger_1.logger.info('Sandbox applied', {
|
|
69
|
+
if (result.applied) {
|
|
70
|
+
logger_1.logger.info('Sandbox applied', {
|
|
71
|
+
pid: process.pid,
|
|
72
|
+
framework: process.frameworkId,
|
|
73
|
+
profile: profile.name,
|
|
74
|
+
features: result.features,
|
|
75
|
+
});
|
|
64
76
|
}
|
|
65
77
|
else {
|
|
66
|
-
logger_1.logger.warn('Sandbox
|
|
78
|
+
logger_1.logger.warn('Sandbox NOT applied — telemetry will report unprotected', {
|
|
79
|
+
pid: process.pid,
|
|
80
|
+
framework: process.frameworkId,
|
|
81
|
+
profile: profile.name,
|
|
82
|
+
reason: result.reason,
|
|
83
|
+
});
|
|
67
84
|
}
|
|
68
85
|
return result;
|
|
69
86
|
}
|
|
70
87
|
catch (err) {
|
|
71
88
|
logger_1.logger.error('Failed to apply sandbox', { pid: process.pid, framework: process.frameworkId, err: err.message });
|
|
72
|
-
return false;
|
|
89
|
+
return { applied: false, features: [], reason: err.message };
|
|
73
90
|
}
|
|
74
91
|
}
|
|
75
92
|
async function removeSandbox(pid) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":";;;;;AA8DA,wDAYC;AAED,oCAoCC;AAED,sCAQC;AAED,kCAEC;AAED,0CAEC;AAlID,4CAAoB;AACpB,oCAAiF;AAEjF,sCAAmC;AACnC,mDAA+C;AAC/C,mDAA+C;AAC/C,uDAAmD;AA+BnD,IAAI,OAAO,GAA0B,IAAI,CAAC;AAE1C,SAAS,UAAU;IACjB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAE5B,MAAM,QAAQ,GAAG,YAAE,CAAC,QAAQ,EAAE,CAAC;IAC/B,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,GAAG,IAAI,4BAAY,EAAE,CAAC;IAC/B,CAAC;SAAM,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,GAAG,IAAI,4BAAY,EAAE,CAAC;IAC/B,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,GAAG,IAAI,gCAAc,EAAE,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,eAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC1E,OAAO,GAAG;YACR,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,QAAQ,mBAAmB,EAAE,CAAC;YAC7G,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK;YAChC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK;YAC9B,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;SAClC,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,sBAAsB,CAAC,WAAmB;IACxD,KAAK,MAAM,OAAO,IAAI,8BAAsB,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/C,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,8BAAsB,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;YAC3C,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAEM,KAAK,UAAU,YAAY,CAAC,OAAsB,EAAE,MAAoB;IAC7E,qEAAqE;IACrE,iDAAiD;IACjD,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,IAAI,qBAAqB,CAAC;IACxE,MAAM,OAAO,GAAG,8BAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC;WACnE,8BAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,qBAAqB,CAAC;WAClE,IAAI,CAAC;IAEV,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,eAAM,CAAC,IAAI,CAAC,0CAA0C,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9G,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;IAC1E,CAAC;IAED,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QACnF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,eAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;gBAC7B,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,SAAS,EAAE,OAAO,CAAC,WAAW;gBAC9B,OAAO,EAAE,OAAO,CAAC,IAAI;gBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,eAAM,CAAC,IAAI,CAAC,yDAAyD,EAAE;gBACrE,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,SAAS,EAAE,OAAO,CAAC,WAAW;gBAC9B,OAAO,EAAE,OAAO,CAAC,IAAI;gBACrB,MAAM,EAAE,MAAM,CAAC,MAAM;aACtB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3H,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;IAC1E,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,EAAE,GAAG,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,WAAW,CAAC,GAAW;IAC3C,OAAO,UAAU,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AACvC,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,OAAO,UAAU,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { SandboxProfile } from '../types';
|
|
2
|
-
import { SandboxBackend } from './index';
|
|
2
|
+
import { SandboxBackend, SandboxResult } from './index';
|
|
3
3
|
export declare class LinuxSandbox implements SandboxBackend {
|
|
4
4
|
private cgroupV2;
|
|
5
5
|
constructor();
|
|
6
6
|
private detectCgroupVersion;
|
|
7
|
+
/**
|
|
8
|
+
* Apply available cgroups limits to an existing PID. Linux kernel primitives
|
|
9
|
+
* for syscall filtering (seccomp), filesystem access (Landlock), and
|
|
10
|
+
* capability dropping all REQUIRE fork-time application — they cannot be
|
|
11
|
+
* retroactively applied to a running process. We report this honestly via
|
|
12
|
+
* the `features` array so telemetry doesn't claim coverage we don't have.
|
|
13
|
+
*
|
|
14
|
+
* For full per-process restrictions, deploy the agent through Sentry's
|
|
15
|
+
* launcher wrapper (planned), which forks → applies seccomp/Landlock/caps
|
|
16
|
+
* → exec()s the agent binary.
|
|
17
|
+
*/
|
|
7
18
|
applySandbox(pid: number, profile: SandboxProfile, defaults: {
|
|
8
19
|
cpuMax: string;
|
|
9
20
|
memoryMax: string;
|
|
10
21
|
pidMax: number;
|
|
11
|
-
}): Promise<
|
|
22
|
+
}): Promise<SandboxResult>;
|
|
12
23
|
private applyCgroupV2;
|
|
13
24
|
private applyCgroupV1;
|
|
14
25
|
private addPidToCgroup;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linux-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/linux-sandbox.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"linux-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/linux-sandbox.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAMxD,qBAAa,YAAa,YAAW,cAAc;IACjD,OAAO,CAAC,QAAQ,CAAU;;IAM1B,OAAO,CAAC,mBAAmB;IAY3B;;;;;;;;;;OAUG;IACG,YAAY,CAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAC9D,OAAO,CAAC,aAAa,CAAC;YAyCX,aAAa;YA8Bb,aAAa;YA+Bb,cAAc;IActB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuB5C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS1C,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC;IA2BnF,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,aAAa;CAKtB"}
|
|
@@ -26,68 +26,102 @@ class LinuxSandbox {
|
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Apply available cgroups limits to an existing PID. Linux kernel primitives
|
|
31
|
+
* for syscall filtering (seccomp), filesystem access (Landlock), and
|
|
32
|
+
* capability dropping all REQUIRE fork-time application — they cannot be
|
|
33
|
+
* retroactively applied to a running process. We report this honestly via
|
|
34
|
+
* the `features` array so telemetry doesn't claim coverage we don't have.
|
|
35
|
+
*
|
|
36
|
+
* For full per-process restrictions, deploy the agent through Sentry's
|
|
37
|
+
* launcher wrapper (planned), which forks → applies seccomp/Landlock/caps
|
|
38
|
+
* → exec()s the agent binary.
|
|
39
|
+
*/
|
|
29
40
|
async applySandbox(pid, profile, defaults) {
|
|
30
41
|
const cgroupPath = path_1.default.join(CGROUP_ROOT, SENTRY_CGROUP_NAME, `sentry-${pid}`);
|
|
42
|
+
const features = [];
|
|
31
43
|
try {
|
|
32
44
|
await promises_1.default.mkdir(cgroupPath, { recursive: true });
|
|
33
45
|
if (this.cgroupV2) {
|
|
34
|
-
await this.applyCgroupV2(cgroupPath, pid, profile, defaults);
|
|
46
|
+
const v2Features = await this.applyCgroupV2(cgroupPath, pid, profile, defaults);
|
|
47
|
+
features.push(...v2Features);
|
|
35
48
|
}
|
|
36
49
|
else {
|
|
37
|
-
await this.applyCgroupV1(cgroupPath, pid, profile, defaults);
|
|
50
|
+
const v1Features = await this.applyCgroupV1(cgroupPath, pid, profile, defaults);
|
|
51
|
+
features.push(...v1Features);
|
|
38
52
|
}
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
41
|
-
|
|
53
|
+
const moved = await this.addPidToCgroup(cgroupPath, pid);
|
|
54
|
+
if (!moved) {
|
|
55
|
+
return {
|
|
56
|
+
applied: false,
|
|
57
|
+
features: [],
|
|
58
|
+
reason: 'Could not move PID into cgroup (insufficient permissions or process exited)',
|
|
59
|
+
};
|
|
42
60
|
}
|
|
43
|
-
|
|
61
|
+
logger_1.logger.info('Linux sandbox configured', { pid, cgroupPath, profile: profile.name, features });
|
|
62
|
+
const strictFeatures = features.filter(feature => /seccomp|landlock|cap/i.test(feature));
|
|
63
|
+
return {
|
|
64
|
+
applied: strictFeatures.length > 0,
|
|
65
|
+
features,
|
|
66
|
+
reason: features.length === 0
|
|
67
|
+
? 'No cgroup controllers were available'
|
|
68
|
+
: strictFeatures.length > 0
|
|
69
|
+
? 'Strict sandbox features applied'
|
|
70
|
+
: 'Applied cgroup resource controls only; seccomp/Landlock/capabilities require fork-time launch',
|
|
71
|
+
};
|
|
44
72
|
}
|
|
45
73
|
catch (err) {
|
|
46
74
|
logger_1.logger.error('Linux sandbox failed', { pid, err: err.message });
|
|
47
|
-
return false;
|
|
75
|
+
return { applied: false, features: [], reason: err.message };
|
|
48
76
|
}
|
|
49
77
|
}
|
|
50
|
-
async applyCgroupV2(cgroupPath,
|
|
78
|
+
async applyCgroupV2(cgroupPath, _pid, profile, defaults) {
|
|
79
|
+
const features = [];
|
|
51
80
|
const cpuMax = profile.cpuMax || defaults.cpuMax;
|
|
52
81
|
const memoryMax = profile.memoryMax || defaults.memoryMax;
|
|
53
82
|
const pidMax = profile.pidMax || defaults.pidMax;
|
|
54
|
-
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'cpu.max'), `${cpuMax}\n`);
|
|
55
|
-
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'memory.max'), this.parseMemoryBytes(memoryMax).toString());
|
|
56
83
|
try {
|
|
57
|
-
await promises_1.default.writeFile(path_1.default.join(cgroupPath, '
|
|
84
|
+
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'cpu.max'), `${cpuMax}\n`);
|
|
85
|
+
features.push('cgroup_v2_cpu');
|
|
58
86
|
}
|
|
59
|
-
catch {
|
|
60
|
-
|
|
87
|
+
catch { /* controller unavailable */ }
|
|
88
|
+
try {
|
|
89
|
+
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'memory.max'), this.parseMemoryBytes(memoryMax).toString());
|
|
90
|
+
features.push('cgroup_v2_memory');
|
|
61
91
|
}
|
|
62
|
-
|
|
92
|
+
catch { /* controller unavailable */ }
|
|
63
93
|
try {
|
|
64
|
-
await promises_1.default.writeFile(
|
|
94
|
+
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'pids.max'), pidMax.toString());
|
|
95
|
+
features.push('cgroup_v2_pids');
|
|
65
96
|
}
|
|
66
|
-
catch {
|
|
67
|
-
|
|
97
|
+
catch { /* pids controller may not be enabled */ }
|
|
98
|
+
try {
|
|
99
|
+
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'io.max'), '8:0 rbps=104857600 wbps=52428800\n');
|
|
100
|
+
features.push('cgroup_v2_io');
|
|
68
101
|
}
|
|
102
|
+
catch { /* io controller may not be available */ }
|
|
103
|
+
return features;
|
|
69
104
|
}
|
|
70
|
-
async applyCgroupV1(cgroupPath,
|
|
105
|
+
async applyCgroupV1(cgroupPath, _pid, profile, defaults) {
|
|
106
|
+
const features = [];
|
|
71
107
|
const memoryMax = profile.memoryMax || defaults.memoryMax;
|
|
72
108
|
try {
|
|
73
109
|
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'memory.limit_in_bytes'), this.parseMemoryBytes(memoryMax).toString());
|
|
110
|
+
features.push('cgroup_v1_memory');
|
|
74
111
|
}
|
|
75
|
-
catch {
|
|
76
|
-
// memory controller may not be mounted
|
|
77
|
-
}
|
|
112
|
+
catch { /* memory controller may not be mounted */ }
|
|
78
113
|
try {
|
|
79
114
|
const cpuCfsQuota = this.parseCpuQuota(profile.cpuMax || defaults.cpuMax);
|
|
80
115
|
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'cpu.cfs_quota_us'), cpuCfsQuota.toString());
|
|
116
|
+
features.push('cgroup_v1_cpu');
|
|
81
117
|
}
|
|
82
|
-
catch {
|
|
83
|
-
// cpu controller may not be mounted
|
|
84
|
-
}
|
|
118
|
+
catch { /* cpu controller may not be mounted */ }
|
|
85
119
|
try {
|
|
86
120
|
await promises_1.default.writeFile(path_1.default.join(cgroupPath, 'pids.max'), (profile.pidMax || defaults.pidMax).toString());
|
|
121
|
+
features.push('cgroup_v1_pids');
|
|
87
122
|
}
|
|
88
|
-
catch {
|
|
89
|
-
|
|
90
|
-
}
|
|
123
|
+
catch { /* pids controller may not be mounted */ }
|
|
124
|
+
return features;
|
|
91
125
|
}
|
|
92
126
|
async addPidToCgroup(cgroupPath, pid) {
|
|
93
127
|
const procsFile = path_1.default.join(cgroupPath, 'cgroup.procs');
|