Skip to content

feat(api): add LogQL query filter to sandbox logs endpoint#2963

Closed
mishushakov wants to merge 6 commits into
mainfrom
mishushakov/sandbox-logs-query
Closed

feat(api): add LogQL query filter to sandbox logs endpoint#2963
mishushakov wants to merge 6 commits into
mainfrom
mishushakov/sandbox-logs-query

Conversation

@mishushakov

Copy link
Copy Markdown
Member

Adds an optional query field to GET /v2/sandboxes/{sandboxID}/logs that is appended verbatim as a LogQL pipeline after the server-built stream selector ({teamID, sandboxID, category!="metrics"}). Since LogQL can't reopen a stream selector mid-pipeline, tenant/sandbox scoping is always enforced server-side and a client's expression can only narrow within its own logs (bounded by a 1024-char cap); it takes precedence over the level/search filters. The field is threaded through the cluster resources layer and the edge V1SandboxLogs contract, with regenerated API/edge/integration clients.

This is split out from #2935 (envd command-output persistence) and can merge independently — the two are decoupled, but together they let callers fetch a single command's output with query=| json | pid="<pid>" | event_type="process_output".

🤖 Generated with Claude Code

Add an optional `query` field to GET /v2/sandboxes/{sandboxID}/logs that is
appended verbatim as a LogQL pipeline after the server-built stream selector
({teamID, sandboxID, category!="metrics"}). Because LogQL cannot reopen a
stream selector mid-pipeline, tenant/sandbox scoping is always enforced
server-side and the client expression can only narrow within its own logs;
a 1024-char cap bounds abuse. The field is threaded through the cluster
resources layer and the edge contract, and takes precedence over level/search.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cla-bot cla-bot Bot added the cla-signed label Jun 9, 2026
@cursor

cursor Bot commented Jun 9, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
User-supplied LogQL is new attack surface for multi-tenant log access; mitigated by AST validation that enforces team/sandbox matchers and maps parse failures to 400.

Overview
Sandbox log APIs gain an optional query parameter so callers can append a custom LogQL pipeline after a server-fixed stream selector that always binds team and sandbox. When query is set it overrides the existing level and search filters, and invalid or scope-breaking expressions are rejected with a client error instead of hitting Loki blindly.

Reviewed by Cursor Bugbot for commit 5a9b568. Bugbot is set up for automated code reviews on this repo. Configure here.

mishushakov added a commit that referenced this pull request Jun 9, 2026
Move the /v2/sandboxes/{sandboxID}/logs `query` field and its supporting
provider/resources/edge changes out to a separate PR (#2963). This branch now
contains only the envd command-output persistence: stdout/stderr is written to
the log pipeline (stamped with pid, capped per command).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The QuerySandboxLogs function swallows errors returned by l.client.QueryRange and ResponseMapper, returning a nil error instead of propagating it. With the introduction of user-customizable LogQL queries, syntax errors are highly likely, and swallowing them will result in a confusing user experience where invalid queries silently return empty logs with a 200 OK status. Please update QuerySandboxLogs to propagate these errors back to the caller.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/shared/pkg/logs/loki/provider.go
@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
2780 5 2775 5
View the top 2 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/proxies::TestEnvdAccessTokenAutoResumeViaProxy
Stack Traces | 10.8s run time
=== RUN   TestEnvdAccessTokenAutoResumeViaProxy
=== PAUSE TestEnvdAccessTokenAutoResumeViaProxy
=== CONT  TestEnvdAccessTokenAutoResumeViaProxy
    traffic_access_token_test.go:357: 
        	Error Trace:	.../tests/proxies/traffic_access_token_test.go:357
        	Error:      	Received unexpected error:
        	            	Get "http://localhost:3002/health": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
        	Test:       	TestEnvdAccessTokenAutoResumeViaProxy
--- FAIL: TestEnvdAccessTokenAutoResumeViaProxy (10.85s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestCommandKillNextApp
Stack Traces | 60.5s run time
=== RUN   TestCommandKillNextApp
=== PAUSE TestCommandKillNextApp
=== CONT  TestCommandKillNextApp
    process_test.go:28: Command [npx] output: event:{start:{pid:1257}}
Executing command /bin/bash in sandbox i0onbi0xvn5admsulsnys
    process_test.go:28: Command [npx] output: event:{data:{stderr:"npm"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" WARN exec The following package was not found and will be installed: create-next-app@16.3.0-preview.0\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"Creating a new Next.js app in ....../home/user/nextapp.\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"Using npm.\n\nInitializing project with template: app-tw \n\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\nInstalling dependencies:\n- next\n- react\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"- react-dom\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\nInstalling devDependencies:\n- @tailwindcss/postcss\n- @types/node\n- @types/react\n- @types/react-dom\n- eslint\n- eslint-config-next\n- tailwindcss\n- typescript\n\n"}}
Executing command /bin/bash in sandbox io1z18lcyftob6myj2tm9 (user: root)
    process_test.go:28: Command [npx] output: event:{data:{stderr:"npm "}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"WARN EBADENGINE Unsupported engine {\nnpm WARN EBADENGINE   package: 'eslint-visitor-keys@5.0.1',\nnpm WARN EBADENGINE   required: { node: '^20.19.0 || ^22.13.0 || >=24' },\nnpm WARN EBADENGINE   current: { node: 'v20.9.0', npm: '10.1.0' }\nnpm WARN EBADENGINE }\n"}}
Executing command cat in sandbox itmqcnrc02ehjwlt0rxlu (user: root)
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\nadded 364 packages, and audited 365 packages in 34s\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\n147 packages are looking for funding\n  run `npm fund` for details\n\nfound 0 vulnerabilities\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"  Downloading swc package @next/swc-linux-x64-gnu... to ....../home/user/.cache/next-swc\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"⨯ Failed to download swc package from https://registry.npmjs.org/@.../swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.0-preview.0.tgz\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"Unhandled Rejection: Error: request failed with status 404\n    at ignore-listed frames\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"Success! Created nextapp at .../home/user/nextapp\n\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stdout:"A new version of `create-next-app` is available!\nYou can update by running: npm i -g create-next-app@preview\n\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"npm "}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"notice"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" \n"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"npm"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" "}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"notice"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" New major version of npm available! 10.1.0 -> 11.16.0\n"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"npm "}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:"notice"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" Changelog: <https://github..../releases/tag/v11.16.0>\nnpm notice"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" Run `npm install -g npm@11.16.0` to update!\nnpm"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" notice"}}
    process_test.go:28: Command [npx] output: event:{data:{stderr:" \n"}}
    process_test.go:28: Command [npx] output: event:{end:{exited:true status:"exit status 0"}}
    process_test.go:28: Command [npx] completed successfully in sandbox i6j3s4pjgsiv620i271vk
    process_test.go:55: dev: event:{start:{pid:1421}}
    process_test.go:55: dev: event:{data:{stdout:"\n> nextapp@0.1.0 dev\n> next dev\n\n"}}
    process_test.go:55: dev: event:{data:{stdout:"▲ Next.js 16.3.0-preview.0 (Turbopack)\n"}}
    process_test.go:55: dev: event:{data:{stdout:"- Local:         http://localhost:3000\n- Network:       http://169.254.0.21:3000\n"}}
    process_test.go:55: dev: event:{data:{stdout:"✓ Ready in 432ms\n"}}
    process_test.go:55: dev: event:{data:{stdout:"  Downloading swc package @next/swc-wasm-nodejs... to ....../home/user/.cache/next-swc\n"}}
    process_test.go:55: dev: event:{data:{stderr:"⨯ Failed to download swc package from https://registry.npmjs.org/@.../swc-wasm-nodejs/-/swc-wasm-nodejs-16.3.0-preview.0.tgz\n"}}
    process_test.go:55: dev: event:{data:{stderr:"⚠ Attempted to load ....../node_modules/next/next-swc-fallback/@next/swc-linux-x64-gnu/next-swc.linux-x64-gnu.node, but it was not installed\n⚠ Attempted to load ....../node_modules/next/next-swc-fallback/@next/swc-linux-x64-musl/next-swc.linux-x64-musl.node, but it was not installed\n⚠ Attempted to load @next/swc-linux-x64-gnu, but it was not installed\n⚠ Attempted to load @next/swc-linux-x64-musl, but it was not installed\n"}}
    process_test.go:55: dev: event:{data:{stderr:"⚠ Error: request failed with status 404\n    at ignore-listed frames\n"}}
    process_test.go:55: dev: event:{data:{stderr:"⚠ Found lockfile missing swc dependencies, patching...\n"}}
    process_test.go:55: dev: event:{data:{stderr:"⨯ Failed to patch lockfile, please try uninstalling and reinstalling next in this workspace\n"}}
    process_test.go:55: dev: event:{data:{stderr:"TypeError: Cannot read properties of undefined (reading 'os')\n    at ignore-listed frames\n"}}
    process_test.go:55: dev: event:{data:{stderr:"⨯ Failed to load SWC binary for linux/x64, see more info here: https://nextjs..../docs/messages/failed-loading-swc\n⨯ Failed to load next.config.ts, see more info here https://nextjs..../docs/messages/next-config-error\n"}}
    process_test.go:55: dev: event:{data:{stderr:"Error: Failed to load SWC binary for linux/x64, see more info here: https://nextjs..../docs/messages/failed-loading-swc\n    at ignore-listed frames\n"}}
    process_test.go:55: dev: event:{data:{stdout:"Attention: Next.js now collects completely anonymous telemetry regarding usage.\n"}}
    process_test.go:55: dev: event:{data:{stdout:"This information is used to shape Next.js' roadmap and prioritize features.\nYou can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:\nhttps://nextjs.org/telemetry\n\n\x1b[?25h\n"}}
    process_test.go:55: dev: event:{end:{exit_code:1 exited:true status:"exit status 1" error:"exit status 1"}}
    process_test.go:79: 
        	Error Trace:	.../tests/envd/process_test.go:79
        	Error:      	"[]" should have 1 item(s), but has 0
        	Test:       	TestCommandKillNextApp
        	Messages:   	Expected one process (next dev) running
--- FAIL: TestCommandKillNextApp (60.52s)
View the full list of 3 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestSandboxListPaginationRunningLargerLimit

Flake rate in main: 39.13% (Passed 949 times, Failed 610 times)

Stack Traces | 95.2s run time
=== RUN   TestSandboxListPaginationRunningLargerLimit
    sandbox_list_test.go:327: Created sandbox 1/12: ilomcpp79ny1bd3ugxizk
    sandbox_list_test.go:327: Created sandbox 2/12: i2ofoq908xta929ndbmpt
    sandbox_list_test.go:327: Created sandbox 3/12: iiicsozs753xmda8zt0vs
    sandbox_list_test.go:327: Created sandbox 4/12: iu694wkjtdad445poxyqi
    sandbox_list_test.go:327: Created sandbox 5/12: i05nmlhola099u9mhoikn
    sandbox_list_test.go:327: Created sandbox 6/12: i4qdhhjqe45c189ypxpqd
    sandbox_list_test.go:327: Created sandbox 7/12: is6vbk0tp1nh8yxn64x71
    sandbox_list_test.go:327: Created sandbox 8/12: iiw9at45c1gbf6k42cgi3
    sandbox_list_test.go:327: Created sandbox 9/12: ialzix7x2z4do7o7yado3
    sandbox_list_test.go:327: Created sandbox 10/12: iwj4s9d1d5w1otn516ngq
    sandbox_list_test.go:327: Created sandbox 11/12: ide3bkdprunwk2h37hfdc
    sandbox_list_test.go:327: Created sandbox 12/12: ihfw8de5gbjjgustviqp0
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:340
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	"[]" should have 12 item(s), but has 0
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:330
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxListPaginationRunningLargerLimit
--- FAIL: TestSandboxListPaginationRunningLargerLimit (95.21s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity

Flake rate in main: 53.89% (Passed 942 times, Failed 1101 times)

Stack Traces | 64.1s run time
=== RUN   TestSandboxMemoryIntegrity
=== PAUSE TestSandboxMemoryIntegrity
=== CONT  TestSandboxMemoryIntegrity
    sandbox_memory_integrity_test.go:27: Build completed successfully
--- FAIL: TestSandboxMemoryIntegrity (64.12s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity/tmpfs_hash

Flake rate in main: 53.91% (Passed 932 times, Failed 1090 times)

Stack Traces | 196s run time
=== RUN   TestSandboxMemoryIntegrity/tmpfs_hash
=== PAUSE TestSandboxMemoryIntegrity/tmpfs_hash
=== CONT  TestSandboxMemoryIntegrity/tmpfs_hash
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{start:{pid:1266}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Total memory: 985 MB\nUsed memory before tmpfs mount: 185 MB\nFree memory before tmpfs mount: 799 MB\nMemory to use in integrity test (60% of free, min 64MB): 479 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"479+0 records in\n479+0 records out\n502267904 bytes (502 MB, 479 MiB) copied, 1.86823 s, 269 MB/s\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\tCommand being timed: \"dd if=/dev/urandom of=/mnt/testfile bs=1M count=479\"\n\tUser time (seconds): 0.00\n\tSystem time (seconds): 1.86\n\tPercent of CPU this job got: 99%\n\tElapsed (wall clock) time (h:mm:ss or m:ss): 0:01.87\n\tAverage shared text size (kbytes): 0\n\tAverage unshared data size (kbytes): 0\n\tAverage stack size (kbytes): 0\n\tAverage total size (kbytes): 0\n\tMaximum resident set size (kbytes): 2632\n\tAverage resident set size (kbytes): 0\n\tMajor (requiring I/O) page faults: 2\n\tMinor (reclaiming a frame) page faults: 347\n\tVoluntary context switches: 3\n\tInvoluntary context switches: 6\n\tSwaps: 0\n\tFile system inputs: 176\n\tFil"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"e system outputs: 0\n\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"Socket m"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"essage"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s s"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ent:"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" 0\n\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"Sock"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"et m"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"essa"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ges "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"rece"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ived"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:": 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\n\tSi"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"gnal"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s de"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"live"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"red: 0\n\tPage size (bytes): 4096\n\tExit status: 0\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Used memory after tmpfs mount and file fill: 668 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] completed successfully in sandbox ijr4d6r26wtae5cgkqx7c
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1282}}
Executing command bash in sandbox ia687mhxni28ixqbrfr7w (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{data:{stdout:"f3255e2227603238b789d605b5815e437a4fb747fe90ae1e11d7a0c26295ca40\n"}}
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:80: Command [bash] completed successfully in sandbox ijr4d6r26wtae5cgkqx7c
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1285}}
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
Executing command bash in sandbox ijr4d6r26wtae5cgkqx7c (user: root)
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:81
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	Received unexpected error:
        	            	failed to execute command bash in sandbox ijr4d6r26wtae5cgkqx7c: unavailable: HTTP status 502 Bad Gateway
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:78
        	            				.../tests/orchestrator/sandbox_memory_integrity_test.go:110
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxMemoryIntegrity/tmpfs_hash
--- FAIL: TestSandboxMemoryIntegrity/tmpfs_hash (196.28s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

mishushakov and others added 2 commits June 9, 2026 23:45
QuerySandboxLogs previously swallowed errors from QueryRange and
ResponseMapper, returning empty logs with a 200 OK. With user-supplied
LogQL queries, syntax errors silently returned empty results.

Propagate those errors, and validate the query locally with Loki's
parser before sending it so a malformed client query returns a helpful
400 with the parse message instead of an opaque 500 (the logcli client
discards the HTTP status and retries 400s, so it can't be classified
from the returned error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The stream selector and structured level/search filters are always
built server-side and sanitized, so trust them by default and skip
parsing them. Validate only when the client supplies a raw pipeline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mishushakov mishushakov marked this pull request as ready for review June 9, 2026 21:57
@mishushakov

Copy link
Copy Markdown
Member Author

@claude review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 73142a90a8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

logger.L().Error(ctx, "error when returning logs for sandbox", zap.Error(err), logger.WithSandboxID(sandboxID))

return make([]logs.LogEntry, 0), nil
return nil, fmt.Errorf("error when returning logs for sandbox: %w", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve best-effort log responses on Loki errors

In local-cluster deployments, any Loki transport error from QueryRange now propagates through resources_local.go as a 500, whereas the previous implementation logged the failure and returned an empty log list. That means a transient Loki outage or timeout will break the sandbox logs endpoint entirely for callers even when the request/query is otherwise valid; only ErrInvalidQuery needs to be surfaced as an error, while the existing best-effort behavior should remain for upstream Loki failures.

Useful? React with 👍 / 👎.

// and filter stages in valid LogQL order, e.g. `| json | pid="1234"` or `|= "error"`.
// This takes precedence over the structured level/search filters.
if pipelineValue := strings.TrimSpace(utils.DerefOrDefault(pipeline, "")); pipelineValue != "" {
return selector + " " + pipelineValue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Agentic Security Review
Severity: HIGH
The new query value is concatenated verbatim after the enforced selector, but there is no guard that the fragment must begin with a pipeline stage. A caller can supply a full binary expression (for example or {category!="metrics"}), which changes the query root from selector + pipeline to selector OR attacker_selector and can bypass sandbox/team scoping.

Impact: An authenticated caller can potentially expand log reads beyond the intended sandbox/team boundary and retrieve unauthorized logs.

Fix in Cursor Fix in Web

Reviewed by Cursor Security Reviewer for commit 73142a9. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dangerous 🔥

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can instruct the parser to avoid any negative selectors perhaps

Only a malformed client query (ErrInvalidQuery) should fail the request.
Transport errors, timeouts, and mapping failures from Loki revert to the
prior best-effort behavior: log and return an empty list, so a transient
Loki outage doesn't break the sandbox logs endpoint for valid requests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mishushakov mishushakov marked this pull request as draft June 9, 2026 22:03

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bc7f8dd. Configure here.

Comment thread packages/shared/pkg/logs/loki/provider.go
A client-supplied pipeline is appended after the enforced stream selector.
Parse the resulting query and verify on the AST that it is a log-selector
query whose stream selector still pins the caller's teamID and sandboxID,
rejecting anything that re-roots the query (e.g. a binary `or` selector or
a metric expression) as ErrInvalidQuery. Defense-in-depth so scoping does
not rely on LogQL grammar invariants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc7f8dd420

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Direction: direction,
Level: logToEdgeLevel(level),
Search: search,
Query: query,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate query filtering on edge support

When the API talks to a remote cluster whose edge service has not yet been rolled to the new spec, this new query parameter is just an unknown query string and will be ignored, so callers using ?query=... can get a 200 with unfiltered logs. The existing compatibility check in this file only verifies the level/search feature header, so mixed-version remote clusters have no failure or warning path for this new filter.

Useful? React with 👍 / 👎.

Add the exact "or {teamID=…, sandboxID=…}" union form (and a metric-union
variant) to the scope-bypass regression suite. These do not parse as valid
LogQL (the `or` operator requires sample-expr operands, not log selectors),
and would also be caught by the AST scope guard; pin them so a future Loki
bump can't silently start accepting them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mishushakov

Copy link
Copy Markdown
Member Author

@claude review

@mishushakov

Copy link
Copy Markdown
Member Author

was a nice experiment, but we don't want to lock ourselves into LogQL.

@mishushakov mishushakov closed this Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant