Skip to content

fix(sidecar): harden band-math eval, job caps, and result bounds - #1574

Merged
giswqs merged 8 commits into
opengeos:mainfrom
Ayush7614:harden/sidecar-eval-and-job-limits
Aug 1, 2026
Merged

fix(sidecar): harden band-math eval, job caps, and result bounds#1574
giswqs merged 8 commits into
opengeos:mainfrom
Ayush7614:harden/sidecar-eval-and-job-limits

Conversation

@Ayush7614

@Ayush7614 Ayush7614 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Block ndarray attribute/file I/O (A.tofile, np.*, etc.) in the raster calculator by AST-whitelisting band-math expressions before eval.
  • Cap concurrent conversion and Whitebox jobs (MAX_IN_FLIGHT_JOBS) with HTTP 429, and enforce the shared feature limit on Whitebox layer_inputs.
  • Bound Sedona SQL result size to MAX_FEATURES, and stop leaking raw conversion/raster job errors to browser-proxied clients.

Test plan

  • uv run --project backend/geolibre_server --extra test pytest for the new conversion/whitebox/raster/sql cases
  • Full test_raster.py -k raster_calculator, test_conversion.py, test_whitebox_endpoints.py, test_sql.py green
  • Confirm a band-math expression like (A.tofile('/tmp/x'), A)[1] fails and writes nothing
  • Confirm a burst of conversion /run calls past the in-flight cap returns 429

Summary by CodeRabbit

  • New Features
    • Added in-flight concurrency caps for background conversion and Whitebox jobs (returns 429 when limits are reached).
    • Enforced maximum embedded GeoJSON layer feature counts for Whitebox (returns 413 when exceeded).
    • Added safer raster band-math evaluation for raster-calc/spectral-index with expression validation.
  • Bug Fixes
    • Conversion and Whitebox failures now return stable, sanitized user-facing errors while full details are logged; failures also clean up reserved job slots when thread startup fails.
  • Tests
    • Expanded 429/413 endpoint coverage, SQL oversized-result rejection, raster calculator sandbox hardening (including file/allocator blocking), and error-scrubbing checks.

Ayush7614 and others added 2 commits July 30, 2026 21:31
Block ndarray attribute I/O in raster calculator expressions, refuse
unbounded in-flight conversion/Whitebox jobs, cap Sedona result size,
sanitize conversion errors, and enforce the shared feature limit on
Whitebox layer inputs.
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Conversion and Whitebox jobs now enforce in-flight limits, embedded inputs are size-bounded, raster expressions undergo AST validation, SQL results are capped, and conversion errors are sanitized in persisted job state.

Changes

Execution and input safeguards

Layer / File(s) Summary
Conversion job controls
backend/geolibre_server/geolibre_server/app/conversion.py, backend/geolibre_server/tests/test_conversion.py
Conversion jobs enforce an in-flight cap, roll back failed thread starts, log raw failures server-side, persist generic error messages, and test these behaviors.
Raster expression validation
backend/geolibre_server/geolibre_server/app/raster.py, backend/geolibre_server/tests/test_raster.py
Raster expressions are parsed with AST validation, unsafe attributes and calls are rejected, container expressions are blocked, and NumPy file-writing and allocation attempts are tested.
Whitebox input and concurrency limits
backend/geolibre_server/geolibre_server/app/whitebox.py, backend/geolibre_server/tests/test_whitebox_endpoints.py
Whitebox requests reject oversized embedded GeoJSON layers with HTTP 413, enforce the in-flight cap with HTTP 429, and clean up failed thread starts.
SQL result bounding
backend/geolibre_server/geolibre_server/sedona_ops.py, backend/geolibre_server/tests/test_sql.py
SQL execution limits converted query results to MAX_FEATURES and tests the resulting HTTP 413 response.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: giswqs

Poem

A rabbit guards the job queue tight,
Counts each task from morn to night.
ASTs chase sneaky calls away,
Big layers hop back without delay,
Safe errors whisper where logs stay.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main hardening changes across band-math eval, job caps, and result bounds.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@Ayush7614
Ayush7614 force-pushed the harden/sidecar-eval-and-job-limits branch from 8e9faad to 01b0b97 Compare July 30, 2026 16:29

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/geolibre_server/geolibre_server/app/conversion.py`:
- Around line 1134-1142: The conversion failure path in _job_update must remove
or redact captured subprocess messages before marking the job failed, while
retaining raw details only in the server-side logger.warning call. Update
backend/geolibre_server/geolibre_server/app/conversion.py:1134-1142 accordingly,
and extend backend/geolibre_server/tests/test_conversion.py:479-509 with a
subprocess/stub that emits a known stderr secret, asserting the secret appears
in neither the job error nor messages.
- Around line 1168-1172: Roll back the newly registered pending job when worker
thread startup raises RuntimeError: in
backend/geolibre_server/geolibre_server/app/conversion.py lines 1168-1172,
update the thread-start path around the conversion job registration to remove
that job under _JOBS_LOCK before propagating the failure; apply the same cleanup
in backend/geolibre_server/geolibre_server/app/whitebox.py lines 1085-1090
around its worker thread startup. Ensure both paths remove only the newly
registered job and preserve normal successful startup behavior.

In `@backend/geolibre_server/geolibre_server/sedona_ops.py`:
- Around line 154-158: Update the query-result handling around
result.to_pandas() to apply LIMIT MAX_FEATURES + 1 before materializing the
result, then use the presence of the extra row to raise SqlInputTooLarge.
Preserve returning at most MAX_FEATURES rows for valid results and avoid
converting the unbounded query result to a full pandas frame.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 309a0fd8-5fbc-4424-a3ce-919f41dc5be4

📥 Commits

Reviewing files that changed from the base of the PR and between 28032d0 and 8e9faad.

📒 Files selected for processing (8)
  • backend/geolibre_server/geolibre_server/app/conversion.py
  • backend/geolibre_server/geolibre_server/app/raster.py
  • backend/geolibre_server/geolibre_server/app/whitebox.py
  • backend/geolibre_server/geolibre_server/sedona_ops.py
  • backend/geolibre_server/tests/test_conversion.py
  • backend/geolibre_server/tests/test_raster.py
  • backend/geolibre_server/tests/test_sql.py
  • backend/geolibre_server/tests/test_whitebox_endpoints.py

Comment thread backend/geolibre_server/geolibre_server/app/conversion.py
Comment thread backend/geolibre_server/geolibre_server/app/conversion.py
Comment thread backend/geolibre_server/geolibre_server/sedona_ops.py
@giswqs

giswqs commented Jul 30, 2026

Copy link
Copy Markdown
Member

/claude-review

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🔍 Cloudflare PR preview

Item Value
Site https://8b0279c5.geolibre-preview.pages.dev
Demo app https://8b0279c5.geolibre-preview.pages.dev/demo/
Commit e72ed61

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🔍 GitHub Pages PR preview

Item Value
Site https://opengeos.org/pages-preview/GeoLibre/pr-1574/
Demo app https://opengeos.org/pages-preview/GeoLibre/pr-1574/demo/
Commit e72ed61

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
backend/geolibre_server/geolibre_server/sedona_ops.py (1)

150-158: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cap the result before materializing it (still unresolved).

result.to_pandas() (Line 153) fully materializes the query result before the MAX_FEATURES check runs. The comment on Lines 155-157 explicitly calls out cross joins/generate_series as row-expanding vectors, yet the code still pulls the entire expanded result into memory before rejecting it — the OOM risk this check is meant to guard against isn't actually mitigated for large/malicious result sets.

This is a previously raised, still-open concern.

🔧 Proposed fix
         result = connection.sql(sql)
+        # Fetch one row past the cap so an oversized result is detected
+        # without materializing more than MAX_FEATURES + 1 rows.
+        result = result.limit(MAX_FEATURES + 1)
         # to_pandas() returns a GeoDataFrame when the result has a geometry
         # column, otherwise a plain DataFrame.
         frame = result.to_pandas()
         if len(frame) > MAX_FEATURES:
             # Input registration already caps each layer, but a query can still
             # expand rows (cross joins, generate_series, etc.). Bound the
             # response the same way vector/PostGIS paths bound payloads.
             raise SqlInputTooLarge(f"Query result exceeds the {MAX_FEATURES}-feature limit")

Please confirm the SedonaDB sql() result object supports .limit() for the pinned apache-sedona==1.7.0 version before applying.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/geolibre_server/geolibre_server/sedona_ops.py` around lines 150 -
158, Update the Sedona query handling around `connection.sql(sql)` to cap rows
before materialization, using the pinned SedonaDB 1.7.0 result API only after
confirming it supports `limit()`. Apply the limit while preserving the existing
`MAX_FEATURES` overflow check and `SqlInputTooLarge` behavior, then call
`to_pandas()` only on the bounded result.
backend/geolibre_server/geolibre_server/app/conversion.py (1)

1158-1190: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Roll back the reserved job slot when worker thread startup fails. Both _start_job (conversion) and whitebox_run register a "pending" JobState under _JOBS_LOCK and then call thread.start() outside the lock with no rollback; a thread-creation failure leaves a permanently stuck slot that counts against MAX_IN_FLIGHT_JOBS forever.

  • backend/geolibre_server/geolibre_server/app/conversion.py#L1158-L1190: wrap thread.start() in try/except and remove the job from _JOBS under _JOBS_LOCK before re-raising on failure.
  • backend/geolibre_server/geolibre_server/app/whitebox.py#L1065-L1102: apply the identical try/except-and-remove cleanup around its thread.start() call.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/geolibre_server/geolibre_server/app/conversion.py` around lines 1158
- 1190, Thread startup can fail after _start_job and whitebox_run reserve
pending jobs, leaving permanent in-flight slots. In
backend/geolibre_server/geolibre_server/app/conversion.py lines 1158-1190, wrap
the _start_job thread.start() call in try/except, remove its job_id from _JOBS
under _JOBS_LOCK, then re-raise; apply the identical cleanup in
backend/geolibre_server/geolibre_server/app/whitebox.py lines 1065-1102 around
whitebox_run’s thread.start().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/geolibre_server/geolibre_server/app/raster.py`:
- Around line 1028-1054: Extend the AST validation around the parsed tree in the
raster expression evaluator to whitelist only the documented scalar/array
arithmetic node types, operators, and permitted names before calling eval.
Reject list/set/dict constructions, exponentiation, unsupported literals, and
any other nodes outside that grammar so expressions such as [0] * 10**9 cannot
allocate resources; preserve the existing curated safe_funcs call validation.

---

Duplicate comments:
In `@backend/geolibre_server/geolibre_server/app/conversion.py`:
- Around line 1158-1190: Thread startup can fail after _start_job and
whitebox_run reserve pending jobs, leaving permanent in-flight slots. In
backend/geolibre_server/geolibre_server/app/conversion.py lines 1158-1190, wrap
the _start_job thread.start() call in try/except, remove its job_id from _JOBS
under _JOBS_LOCK, then re-raise; apply the identical cleanup in
backend/geolibre_server/geolibre_server/app/whitebox.py lines 1065-1102 around
whitebox_run’s thread.start().

In `@backend/geolibre_server/geolibre_server/sedona_ops.py`:
- Around line 150-158: Update the Sedona query handling around
`connection.sql(sql)` to cap rows before materialization, using the pinned
SedonaDB 1.7.0 result API only after confirming it supports `limit()`. Apply the
limit while preserving the existing `MAX_FEATURES` overflow check and
`SqlInputTooLarge` behavior, then call `to_pandas()` only on the bounded result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4d91408c-7cd0-4360-81c9-a1a47cae1a68

📥 Commits

Reviewing files that changed from the base of the PR and between 8e9faad and 01b0b97.

📒 Files selected for processing (8)
  • backend/geolibre_server/geolibre_server/app/conversion.py
  • backend/geolibre_server/geolibre_server/app/raster.py
  • backend/geolibre_server/geolibre_server/app/whitebox.py
  • backend/geolibre_server/geolibre_server/sedona_ops.py
  • backend/geolibre_server/tests/test_conversion.py
  • backend/geolibre_server/tests/test_raster.py
  • backend/geolibre_server/tests/test_sql.py
  • backend/geolibre_server/tests/test_whitebox_endpoints.py

Comment thread backend/geolibre_server/geolibre_server/app/raster.py
Comment thread backend/geolibre_server/geolibre_server/app/conversion.py
Comment thread backend/geolibre_server/geolibre_server/sedona_ops.py
@github-actions

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • None found in the changed logic itself (job-cap counting, AST whitelist, and feature-limit checks all look correct and consistent with existing patterns).

Security

  • High confidenceconversion.py's new error sanitization (_run_conversion_job, ~line 1133) only redacts the error field; the raw subprocess stdout/stderr (via _append_job_message) is still accumulated in job.messages and returned unfiltered by GET /conversion/jobs/{job_id}. The exact "DuckDB/GDAL stderr with absolute paths" content this change targets is still reachable through the same response object. The new test only covers a pre-subprocess exception path, not the realistic subprocess-stderr path, so it doesn't catch this gap. (inline comment posted)
  • Medium confidencesedona_ops.py's new MAX_FEATURES check on the query result (~line 154) runs after result.to_pandas() has already materialized the full result set. It bounds the response payload but not the compute/memory cost of an expanding query (cross join, generate_series, etc.), so it only partially achieves the stated DoS-hardening goal. (inline comment posted)
  • The raster-calculator AST whitelist (raster.py) looks solid: it rejects any Attribute node and any Call whose target isn't one of the curated safe_funcs, which correctly blocks A.tofile(...), dunder-chain escapes, and indirect calls to stripped builtins — I couldn't find a bypass.

Performance

  • Covered above (Sedona result-size check).

Quality

  • Low confidence / minor: replacing all conversion-job failures with one generic message ("Conversion failed. See the sidecar logs for details.") discards previously-useful, non-sensitive detail (e.g. timeout messages) for every failure type, not just ones carrying sensitive paths. This mirrors an existing pattern already used in whitebox.py, so it's a consistent but blunt trade-off rather than a new regression.

CLAUDE.md

  • No violations noted; changes are backend-only Python and don't touch any of the mirrored-constant/menu-catalog/i18n conventions called out in CLAUDE.md.

Clear conversion job messages on failure, roll back pending slots when
Thread.start fails, cap Sedona results before to_pandas, and reject
list/dict AST nodes in band math.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
backend/geolibre_server/geolibre_server/app/raster.py (1)

1043-1060: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Complete the AST allowlist.

The denylist still permits b"x" * 1_000_000_000 and (0,) * 1_000_000_000; both can allocate unbounded memory before the shape check. Reject ast.Tuple and non-numeric constants, and whitelist permitted node/operator types rather than only blocking selected containers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/geolibre_server/geolibre_server/app/raster.py` around lines 1043 -
1060, Update the AST validation around the existing container/comprehension
rejection to use an explicit allowlist of permitted expression and operator node
types for curated band math. Reject ast.Tuple and any non-numeric ast.Constant,
while preserving only the numeric constants, names, calls, and operators
required by the supported expressions so large bytes or tuple multiplication
cannot allocate memory before shape validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/geolibre_server/tests/test_sql.py`:
- Around line 143-144: Update the test stub’s limit method to record the
requested n value, then assert after run_sql() that it equals MAX_FEATURES + 1.
Ensure the test fails when run_sql() omits the limit or supplies an incorrect
bound.

---

Duplicate comments:
In `@backend/geolibre_server/geolibre_server/app/raster.py`:
- Around line 1043-1060: Update the AST validation around the existing
container/comprehension rejection to use an explicit allowlist of permitted
expression and operator node types for curated band math. Reject ast.Tuple and
any non-numeric ast.Constant, while preserving only the numeric constants,
names, calls, and operators required by the supported expressions so large bytes
or tuple multiplication cannot allocate memory before shape validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d1352abb-a6ef-4662-af6e-b29f2c0687a3

📥 Commits

Reviewing files that changed from the base of the PR and between 01b0b97 and 57d0c57.

📒 Files selected for processing (6)
  • backend/geolibre_server/geolibre_server/app/conversion.py
  • backend/geolibre_server/geolibre_server/app/raster.py
  • backend/geolibre_server/geolibre_server/app/whitebox.py
  • backend/geolibre_server/geolibre_server/sedona_ops.py
  • backend/geolibre_server/tests/test_conversion.py
  • backend/geolibre_server/tests/test_sql.py

Comment thread backend/geolibre_server/tests/test_sql.py Outdated
Reject bytes/tuple allocations before eval, and lock the SQL overflow
test to MAX_FEATURES + 1 so the bound cannot drift silently.
@Ayush7614
Ayush7614 force-pushed the harden/sidecar-eval-and-job-limits branch from 58b0afa to 4e39697 Compare July 30, 2026 20:41

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/geolibre_server/geolibre_server/app/raster.py`:
- Around line 1037-1068: Update raster expression validation around
_allowed_nodes and the AST-checking logic to statically inspect ast.BinOp nodes
using ast.Pow. Reject nested exponentiation chains and reject literal exponents
whose magnitude exceeds the permitted limit before native eval() runs, while
preserving existing validation for other binary operations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9446ae8b-991d-4afb-88bf-e7bb5dc27e56

📥 Commits

Reviewing files that changed from the base of the PR and between 58b0afa and 4e39697.

📒 Files selected for processing (7)
  • backend/geolibre_server/geolibre_server/app/conversion.py
  • backend/geolibre_server/geolibre_server/app/raster.py
  • backend/geolibre_server/geolibre_server/app/whitebox.py
  • backend/geolibre_server/geolibre_server/sedona_ops.py
  • backend/geolibre_server/tests/test_conversion.py
  • backend/geolibre_server/tests/test_raster.py
  • backend/geolibre_server/tests/test_sql.py

Comment thread backend/geolibre_server/geolibre_server/app/raster.py
giswqs added 2 commits July 31, 2026 18:30
Resolve the test_whitebox_endpoints.py conflict by keeping both sides:
main's COG-conversion tests for _ensure_raster_outputs_are_cogs and this
branch's in-flight job cap / oversized layer input tests.
- raster.py: cap `**` in band math. The AST allowlist permitted ast.Pow with
  no bound, so `9**9**6` or `9**999999` built a multi-megabyte exact integer
  and pinned a core for tens of seconds before the post-eval shape check could
  reject it. An exponent must now be a numeric literal within MAX_POW_EXPONENT
  (64, far past squares/gamma/roots) unless it derives from a band or curated
  call, where NumPy's element-wise float power is bounded by the raster shape.
- tests/test_raster.py: cover chained (`9**9**6`), oversized (`9**999999`),
  and computed (`9 ** (500 * 500)`) exponents, plus a positive case asserting
  ordinary powers including a negative literal exponent still evaluate.
@giswqs

giswqs commented Jul 31, 2026

Copy link
Copy Markdown
Member

/claude-review

Comment thread backend/geolibre_server/geolibre_server/app/conversion.py
Comment thread backend/geolibre_server/tests/test_raster.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • conversion.py _run_conversion_job's failure handler (lines ~1133-1152) clears both error and messages on every conversion/raster job failure, not just ones at risk of leaking a path. Both RasterToolsDialog.tsx and ConversionDialog.tsx render job.error/job.messages as the only user-facing failure feedback, so this also swallows this PR's own new, carefully-worded AST validation messages from raster.py ("Expression may not use Attribute ...", "Exponent may not exceed 64 ...") plus pre-existing dimension/CRS/missing-band checks — users now see only "Conversion failed. See the sidecar logs for details." with no actionable info, even for input mistakes they could otherwise fix themselves. whitebox.py's equivalent handler deliberately preserves messages for this reason; conversion.py doesn't. Confidence: medium-high.

Security

  • The new AST allowlist in raster.py (band-math eval hardening) looks solid on close inspection: ast.Attribute is unconditionally rejected (blocks A.tofile/A.dump), disallowed Call targets are rejected via a Name-only allowlist, List/Tuple/non-numeric Constant nodes are rejected (blocks pre-allocation DoS), and the ** exponent cap is checked per-node via ast.walk so it can't be hidden inside unreachable branches or nested UnaryOp/parens. The "exponent may skip the cap if band/call-derived" exception is safe because any such exponent forces NumPy's float-elementwise pow path rather than Python's exact-bignum integer pow, so it can't reintroduce the 9**9**6-style stall. No bypass found. Confidence: medium (relies on safe_funcs never gaining a function that returns a plain Python int/large object — worth a comment if that constraint matters long-term).
  • Whitebox/Sedona/conversion caps (429 in-flight limit, 413 feature limit, SqlInputTooLarge on oversized query results) are implemented correctly and consistently, with proper lock scoping and rollback of a reserved job slot when Thread.start() fails. No injection or unsafe-input issues found in the diff.

Performance

  • No new inefficiencies found. _count_in_flight_jobs_locked is O(n) over _JOBS (capped at MAX_RETAINED_JOBS = 100) under the lock — negligible at this scale.
  • sedona_ops.run_sql's .limit(MAX_FEATURES + 1) before .to_pandas() is a good fix for the "materialize an unbounded cross-join" risk, assuming (plausible, unverified without network access) Sedona's DataFrame API pushes the limit down before execution.

Quality

  • Minor: test_raster.py lines 641 and 1060 assert on a message string ("may not access attributes") that the implementation never actually produces — dead assertion branch, flagged inline with a suggestion to tighten it.
  • Minor (not inline): _count_in_flight_jobs_locked is duplicated verbatim in conversion.py and whitebox.py; could be a shared helper, but low priority given the modules are otherwise independent job registries.

CLAUDE.md

  • No violations found. Changes are confined to the Python sidecar (backend/geolibre_server), no touched files fall under the frontend/i18n/plugin/menu-catalog conventions that CLAUDE.md calls out.

giswqs added 2 commits July 31, 2026 18:52
- conversion.py: run each conversion/raster script through a small driver
  that re-emits a `SystemExit` message on a marker line. The failure handler
  previously scrubbed `error` *and* `messages` for every failure, which also
  swallowed this PR's own curated validation text ("Expression may not use
  Attribute ...", "Exponent may not exceed 64 ...", dimension/CRS checks).
  Both RasterToolsDialog and ConversionDialog render `job.error` as the only
  failure feedback, so a user who mistyped a band-math expression saw just
  "See the sidecar logs for details" with no way to read those logs. Curated
  messages now surface verbatim; tracebacks and streamed subprocess output
  stay generic, so the absolute-path leak remains closed.
- test_raster.py: drop the "may not access attributes" assertion the
  implementation never emits and assert the exact messages instead —
  "Call to 'Attribute' is not allowed in band math" for `np.where(...)`, and
  "Expression may not use Subscript" for the tuple-wrapped `A.tofile(...)`.
  Add a bare `A.tofile(...)` case so the test actually covers attribute
  access rather than passing incidentally on the subscript wrapper.
- test_conversion.py: cover the new split — a curated SystemExit reaches
  the client, an unexpected exception stays generic, and scripts still read
  their params from `sys.argv[1]` through the driver.
@giswqs
giswqs merged commit a9699eb into opengeos:main Aug 1, 2026
18 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants