Skip to content

fix: support response query filtering in safe mode#7441

Merged
bijin-bruno merged 4 commits intousebruno:mainfrom
abhishek-bruno:fix/safe-mode-response-query
Apr 2, 2026
Merged

fix: support response query filtering in safe mode#7441
bijin-bruno merged 4 commits intousebruno:mainfrom
abhishek-bruno:fix/safe-mode-response-query

Conversation

@abhishek-bruno
Copy link
Copy Markdown
Member

@abhishek-bruno abhishek-bruno commented Mar 11, 2026

Description

  • Forward filter/predicate arguments from the QuickJS sandbox res() shim to the host query engine, fixing res('path[?].prop', filterFn) in safe mode
  • Add toHostQueryArg helper that marshals QuickJS function handles into native callbacks across the VM boundary, while passing object predicates as-is
  • Remove unnecessary try/finally argument disposal that was inconsistent with other shims in the codebase

Closes #7387
Built on top of #7417 by @cryst-hq

JIRA

How it works

The res() function in the QuickJS sandbox previously only forwarded the expression string to the host, silently dropping any filter/predicate arguments. This caused [?] array filtering to fail with "missing function for resources".

The fix wraps each QuickJS function argument into a native callback that marshals data across the VM boundary (host → sandbox → host), while keeping the filter function safely inside the sandbox. Object predicates (e.g. { id: 'test' }) are simply dumped to plain JS objects.

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • Extended the response function to accept and process query arguments, enabling callback-based filtering with proper error handling.
  • Tests

    • Added comprehensive test suite validating query argument handling, callback execution, and object predicate support.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 36c8a5f9-5784-4a0a-b403-bbceaf94a0ac

📥 Commits

Reviewing files that changed from the base of the PR and between 4548525 and e8ae806.

📒 Files selected for processing (1)
  • packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js
✅ Files skipped from review due to trivial changes (1)
  • packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js

Walkthrough

The pull request adds support for passing callback functions as query arguments to the res() function in the Bruno JavaScript sandbox. A new toHostQueryArg() helper marshals QuickJS function values into native callbacks with proper error handling and resource cleanup.

Changes

Cohort / File(s) Summary
Response shim implementation
packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js
Introduces toHostQueryArg(vm, arg) to convert QuickJS values into host-call arguments, handling function callbacks with marshaling and error propagation. Extends res() signature to accept variadic query arguments that are converted and passed to the host response function.
Response shim tests
packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js
Adds comprehensive Jest test suite validating callback support in res(). Tests verify function and object predicate handling with proper VM context setup, cleanup, and mock assertion of expression and filtering logic.

Sequence Diagram

sequenceDiagram
    participant VM as QuickJS VM
    participant Shim as res() Shim
    participant Host as Host Runtime
    
    VM->>Shim: res(exprStr, callbackFunc)
    Shim->>Shim: toHostQueryArg(vm, callbackFunc)
    Note over Shim: Convert JS function to<br/>native callback
    
    Shim->>Host: res(dumpedExpr, nativeCallback)
    Host->>nativeCallback: invoke(item)
    nativeCallback->>VM: callFunction(callbackFunc, item)
    VM-->>nativeCallback: result
    nativeCallback->>Host: return filtered result
    
    Host-->>Shim: hostResult
    Shim->>VM: marshallToVm(hostResult)
    Shim-->>VM: response value
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

size/XL

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno
  • bijin-bruno

Poem

Callbacks now flow through the sand, 🏜️
Query filters across the land,
Where functions once caused errors to bloom,
Now resources dance without gloom. 💫

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the main objective: enabling response query filtering in safe mode by forwarding filter/predicate arguments.
Linked Issues check ✅ Passed The PR successfully addresses issue #7387 by implementing toHostQueryArg to marshal QuickJS function handles into native callbacks, restoring response query filtering support in safe mode.
Out of Scope Changes check ✅ Passed All changes are focused on fixing response query filtering: toHostQueryArg helper, extended res() shim signature, and comprehensive test coverage—no out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js (1)

76-77: Consider adding error propagation test.

The toHostQueryArg error handling path (when the sandbox callback throws) isn't exercised. A test verifying error propagation would strengthen coverage.

💡 Example test case
it('propagates errors from sandbox callbacks', () => {
  const response = Object.assign(
    () => { throw new Error('should not reach'); },
    { status: 200, statusText: 'OK', headers: {}, body: {} }
  );

  addBrunoResponseShimToContext(vm, response);

  const result = vm.evalCode(`
    res('items[?]', () => { throw new Error('callback error'); })
  `);

  expect(result.error).toBeDefined();
  const error = vm.dump(result.error);
  result.error.dispose();
  expect(error.message).toBe('callback error');
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js` around
lines 76 - 77, Add a test in bruno-response.spec.js that exercises the
toHostQueryArg error path by creating a Bruno response shim with a callback that
throws and calling addBrunoResponseShimToContext(vm, response); evaluate code
via vm.evalCode using a query like res('items[?]', () => { throw new
Error('callback error'); }) and assert that result.error is defined, dump the
error with vm.dump(result.error), dispose it, and expect the dumped
error.message to equal the thrown message; this will verify error propagation
from the sandbox callback (use the existing patterns around vm,
addBrunoResponseShimToContext, vm.evalCode, vm.dump, and
result.error.dispose()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js`:
- Around line 16-22: The tests currently only null out vm and module which leaks
QuickJS WASM resources; update the teardown handlers to explicitly free
resources before nulling: in afterEach, call vm.dispose() (or
vm.destroy()/vm.free() if that’s the API) guarded by a typeof check (e.g., if
(vm && typeof vm.dispose === 'function') vm.dispose()), then set vm = null;
similarly in afterAll call module.dispose()/module.destroy()/module.free()
guarded by a check before setting module = null; adjust the handlers that
reference vm and module to perform disposal first.

---

Nitpick comments:
In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js`:
- Around line 76-77: Add a test in bruno-response.spec.js that exercises the
toHostQueryArg error path by creating a Bruno response shim with a callback that
throws and calling addBrunoResponseShimToContext(vm, response); evaluate code
via vm.evalCode using a query like res('items[?]', () => { throw new
Error('callback error'); }) and assert that result.error is defined, dump the
error with vm.dump(result.error), dispose it, and expect the dumped
error.message to equal the thrown message; this will verify error propagation
from the sandbox callback (use the existing patterns around vm,
addBrunoResponseShimToContext, vm.evalCode, vm.dump, and
result.error.dispose()).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5c91273-0f45-43f0-9102-5b2ad712877c

📥 Commits

Reviewing files that changed from the base of the PR and between 803b3f0 and f50fe9a.

📒 Files selected for processing (3)
  • .gitignore
  • packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js
  • packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js

Comment thread packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js
cryst-hq and others added 2 commits March 24, 2026 12:45
…ndling

- Introduced a new test suite for the bruno-response shim to validate query filtering and function callback behavior.
- Refactored the `res` function to enhance argument handling, allowing for better marshalling of QuickJS function handles and other values.
- Improved error handling within the response processing to ensure robust behavior during query execution.
@abhishek-bruno abhishek-bruno force-pushed the fix/safe-mode-response-query branch from f50fe9a to b731106 Compare March 24, 2026 07:15
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js (1)

16-22: ⚠️ Potential issue | 🟡 Minor

Dispose the QuickJS VM and module in teardown.

Lines 16-22 only clear the JS references. The QuickJS context and WASM module stay allocated until they are explicitly disposed, so repeated test runs will leak memory.

♻️ Suggested teardown change
  afterEach(() => {
-    vm = null;
+    if (vm && typeof vm.dispose === 'function') {
+      vm.dispose();
+    }
+    vm = null;
  });

  afterAll(() => {
-    module = null;
+    if (module && typeof module.dispose === 'function') {
+      module.dispose();
+    }
+    module = null;
  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js` around
lines 16 - 22, The teardown currently just nulls vm and module, leaking the
QuickJS context and WASM module; update the teardown so you call the QuickJS
disposal methods (e.g., vm.dispose() in afterEach and module.dispose() in
afterAll) with null-checks before setting vm = null and module = null; locate
the vm and module variables in bruno-response.spec.js and replace the simple
null assignments with guarded dispose calls to free the VM and WASM module
resources.
🧹 Nitpick comments (2)
packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js (1)

3-7: Use JSDoc for toHostQueryArg().

This helper carries a non-obvious borrowing contract, so please promote the banner comment to JSDoc on the function itself and include the synchronous-lifetime caveat there. As per coding guidelines, "Add JSDoc comments to abstractions for additional details".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js` around lines 3
- 7, Convert the leading banner comment into a JSDoc block placed immediately
above the toHostQueryArg function: document what the function does (marshal a
QuickJS query argument to a host-compatible value), note that function handles
are wrapped as native callbacks and other values are returned as-is, and
explicitly state the borrowing contract and synchronous-lifetime caveat ("Safe
because `@usebruno/query`'s get() invokes filters synchronously, so the borrowed
arg handle is still valid"). Keep the existing details but format them as JSDoc
on the toHostQueryArg declaration to satisfy the coding guideline.
packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js (1)

24-76: Add a thrown-predicate regression test.

The new shim's hardest branch is the error path when the sandbox predicate throws, but the suite only exercises successful filtering. A case that asserts res(...) surfaces that failure would lock down the cross-VM degraded path that is easiest to regress. Based on learnings, "Cover both the 'happy path' and the realistically problematic paths. Validate expected success behaviour, but also validate error handling, edge cases, and degraded-mode behaviour".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js` around
lines 24 - 76, Add a new spec in bruno-response.spec.js that exercises the
shim's error path by making the sandbox predicate throw and asserting that
res(...) surfaces that error; specifically, reuse the pattern around
addBrunoResponseShimToContext(vm, response) and vm.evalCode('res(...)') but
define the response function (the callable assigned via Object.assign) so its
second argument (predicate/filter) throws (or invokes the predicate which
throws) and then assert the vm.unwrapResult/throwing behavior or that the
evalCode result represents the error as expected; locate the existing tests
using addBrunoResponseShimToContext, vm.evalCode, vm.unwrapResult, vm.dump and
mirror their setup while replacing the predicate with one that throws to lock
down the degraded/error path handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js`:
- Around line 9-12: The predicate is being invoked with vm.global as the thisArg
which changes semantics; update the callback returned by the function using
marshallToVm and vm.callFunction to pass vm.undefined instead of vm.global so
predicates run as bare functions (this === undefined) matching the host
filterOrMap behavior; locate the call site where vm.callFunction(arg, vm.global,
itemHandle) is used and replace the second argument with vm.undefined (keep
marshallToVm and dispose logic intact).

---

Duplicate comments:
In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js`:
- Around line 16-22: The teardown currently just nulls vm and module, leaking
the QuickJS context and WASM module; update the teardown so you call the QuickJS
disposal methods (e.g., vm.dispose() in afterEach and module.dispose() in
afterAll) with null-checks before setting vm = null and module = null; locate
the vm and module variables in bruno-response.spec.js and replace the simple
null assignments with guarded dispose calls to free the VM and WASM module
resources.

---

Nitpick comments:
In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js`:
- Around line 3-7: Convert the leading banner comment into a JSDoc block placed
immediately above the toHostQueryArg function: document what the function does
(marshal a QuickJS query argument to a host-compatible value), note that
function handles are wrapped as native callbacks and other values are returned
as-is, and explicitly state the borrowing contract and synchronous-lifetime
caveat ("Safe because `@usebruno/query`'s get() invokes filters synchronously, so
the borrowed arg handle is still valid"). Keep the existing details but format
them as JSDoc on the toHostQueryArg declaration to satisfy the coding guideline.

In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js`:
- Around line 24-76: Add a new spec in bruno-response.spec.js that exercises the
shim's error path by making the sandbox predicate throw and asserting that
res(...) surfaces that error; specifically, reuse the pattern around
addBrunoResponseShimToContext(vm, response) and vm.evalCode('res(...)') but
define the response function (the callable assigned via Object.assign) so its
second argument (predicate/filter) throws (or invokes the predicate which
throws) and then assert the vm.unwrapResult/throwing behavior or that the
evalCode result represents the error as expected; locate the existing tests
using addBrunoResponseShimToContext, vm.evalCode, vm.unwrapResult, vm.dump and
mirror their setup while replacing the predicate with one that throws to lock
down the degraded/error path handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a02fb93f-b53b-40ea-8b34-19e47d90e62f

📥 Commits

Reviewing files that changed from the base of the PR and between f50fe9a and b731106.

📒 Files selected for processing (3)
  • .gitignore
  • packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js
  • packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js
✅ Files skipped from review due to trivial changes (1)
  • .gitignore

Comment thread packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js
…llbacks

- Updated the `toHostQueryArg` function to use `vm.undefined` instead of `vm.global` when calling the provided function argument. This change ensures proper context handling during function execution, improving the reliability of query argument processing.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js (1)

3-7: Use JSDoc for the toHostQueryArg abstraction.

Please convert this block comment to a /** ... */ JSDoc comment so the contract/assumptions are captured in the project’s standard abstraction docs style.

As per coding guidelines "Add JSDoc comments to abstractions for additional details".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js` around lines 3
- 7, Convert the existing block comment above the toHostQueryArg function into a
JSDoc-style comment (/** ... */) preserving the same description and assumptions
(that it marshals a QuickJS query argument to a host-compatible value, that
function handles are wrapped as native callbacks, other values are dumped as-is,
and the safety note about `@usebruno/query`'s get() invoking filters synchronously
so the borrowed arg handle remains valid); attach the JSDoc directly above the
toHostQueryArg declaration and keep the wording clear and concise to match
project abstraction docs style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js`:
- Around line 3-7: Convert the existing block comment above the toHostQueryArg
function into a JSDoc-style comment (/** ... */) preserving the same description
and assumptions (that it marshals a QuickJS query argument to a host-compatible
value, that function handles are wrapped as native callbacks, other values are
dumped as-is, and the safety note about `@usebruno/query`'s get() invoking filters
synchronously so the borrowed arg handle remains valid); attach the JSDoc
directly above the toHostQueryArg declaration and keep the wording clear and
concise to match project abstraction docs style.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7457a268-9349-4ea0-b29f-6c7ef878de94

📥 Commits

Reviewing files that changed from the base of the PR and between b731106 and 4548525.

📒 Files selected for processing (1)
  • packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.js

Copy link
Copy Markdown
Collaborator

@sanish-bruno sanish-bruno left a comment

Choose a reason for hiding this comment

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

PR Review

Found 2 issue(s): 0 major, 0 minor, 2 nits.

Summary

  • The core fix in toHostQueryArg is well-implemented — correctly marshals QuickJS function handles into native callbacks for synchronous get() invocations, and properly handles both function and object-predicate arguments.
  • Good error handling with proper disposal of VM handles on both success and error paths.
  • The .gitignore change introduces duplicate entries.
  • Tests cover both callback and object-predicate paths but have a minor cleanup gap.

Comment thread .gitignore Outdated
Comment thread packages/bruno-js/src/sandbox/quickjs/shims/bruno-response.spec.js
…se tests

- Removed redundant entries from .gitignore to streamline ignored files.
- Improved error handling in the `afterEach` and `afterAll` hooks of the bruno-response tests to ensure proper disposal of resources, preventing potential memory leaks.
@bijin-bruno bijin-bruno merged commit 9465de0 into usebruno:main Apr 2, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Response query filtering does not work

4 participants