| 296 | 296 | | interceptHttps = true; |
| 297 | 297 | | static { |
| 298 | 298 | | // Assigned, not declared: a class field would hide the setter that |
| 299 | | − | // registers the handler with the containers library. `ABUSE_HOST`, |
| 300 | | − | // named up front, is the only host intercepted on its own, so every |
| 301 | | − | // other request goes straight out. |
| 299 | + | // registers the handler with the containers library. Which one a |
| 300 | + | // sandbox uses is chosen as it starts (`run`). |
| 302 | 301 | | AttemptSandbox.outboundHandlers = { egress, abuse }; |
| 303 | | − | AttemptSandbox.outboundByHost = { [ABUSE_HOST]: abuse }; |
| 304 | 302 | | } |
| 305 | 303 | | |
| 306 | 304 | | async run(request: RunRequest): Promise<void> { |
| ⋯ |
| 364 | 362 | | } |
| 365 | 363 | | return; |
| 366 | 364 | | } |
| 367 | | − | // Every sandbox reaches the internet. Its requests go straight out, |
| 368 | | − | // and only `ABUSE_HOST` is intercepted (`outboundByHost`), unless |
| 369 | | − | // it is a workflow job that may be given brokered secrets: then its |
| 370 | | − | // HTTP and HTTPS pass `egress`, which fills them in from the job's |
| 371 | | − | // stand-ins (asked for with the job's token) and sends every |
| 372 | | − | // request on, to any host. Other protocols go straight out. |
| 373 | | − | // Cloudflare Containers intercept either the hosts a class names |
| 374 | | − | // up front or, once a handler is chosen at run time, everything, so |
| 375 | | − | // the hosts of one job's secrets cannot be singled out. |
| 365 | + | // Every sandbox reaches the internet. A workflow job that may be |
| 366 | + | // given brokered secrets sends its HTTP and HTTPS through `egress`, |
| 367 | + | // which fills them in from the job's stand-ins (asked for with the |
| 368 | + | // job's token) and sends every request on, to any host; any other |
| 369 | + | // sandbox has only `ABUSE_HOST` intercepted, so it can report that it |
| 370 | + | // stopped itself for mining. Both are chosen here, as it starts: hosts |
| 371 | + | // named on the class up front (`outboundByHost` in a static block) |
| 372 | + | // left sandboxes that never picked up their jobs (rolled back |
| 373 | + | // 2026-10-13). |
| 376 | 374 | | const brokering = run.kind === "actions" && throughWorker(build, this.env.EGRESS); |
| 377 | 375 | | this.enableInternet = true; |
| 378 | 376 | | if (brokering && run.kind === "actions") { |
| 379 | 377 | | await this.setOutboundHandler("egress", { job: { id: run.jobId, token: run.token } }); |
| 378 | + | } else if (this.env.EGRESS?.trim().toLowerCase() !== "off") { |
| 379 | + | await this.setOutboundByHost(ABUSE_HOST, "abuse").catch((error: unknown) => |
| 380 | + | console.log("abuse reports not routed", String(error)), |
| 381 | + | ); |
| 380 | 382 | | } |
| 381 | 383 | | const harness = guard && !build ? harnessEnv(guard, vars) : {}; |
| 382 | 384 | | // Its HTTPS is re-signed as it passes the Worker, so it trusts that |
| ⋯ |
| 765 | 767 | | export class Sandbox2Core extends AttemptSandbox { |
| 766 | 768 | | static { |
| 767 | 769 | | Sandbox2Core.outboundHandlers = { egress, abuse }; |
| 768 | | − | Sandbox2Core.outboundByHost = { [ABUSE_HOST]: abuse }; |
| 769 | 770 | | } |
| 770 | 771 | | } |
| 771 | 772 | | export class Sandbox4Core extends AttemptSandbox { |
| 772 | 773 | | static { |
| 773 | 774 | | Sandbox4Core.outboundHandlers = { egress, abuse }; |
| 774 | | − | Sandbox4Core.outboundByHost = { [ABUSE_HOST]: abuse }; |
| 775 | 775 | | } |
| 776 | 776 | | } |
| 777 | 777 | | |