Skip to content

fix: don't import broken agents#1278

Merged
UladzislauK-Writer merged 2 commits intodevfrom
vlad/AB-966
Feb 4, 2026
Merged

fix: don't import broken agents#1278
UladzislauK-Writer merged 2 commits intodevfrom
vlad/AB-966

Conversation

@UladzislauK-Writer
Copy link
Collaborator

@UladzislauK-Writer UladzislauK-Writer commented Feb 4, 2026

Summary by CodeRabbit

  • New Features

    • Import confirmation modal now shows an error section with summary and optional details/trace.
  • Improvements

    • Import process includes validation before applying changes to prevent bad uploads.
    • Import/export error responses standardized to include structured summary (and details when available) for clearer messages.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds UI rendering for structured import errors, changes backend import endpoints to return JSON error payloads with summary/details, and adds a pre-apply file validation step via wf_project.read_files() during zip import.

Changes

Cohort / File(s) Summary
Import Error UI
src/ui/src/builder/BuilderHeaderMoreDropdown.vue
Adds import error container, reactive importErrorSummary and importErrorDetails state, conditional rendering of summary/details, clears state on cancel/start, and new CSS for the error block.
Import Validation
src/writer/app_runner.py
Calls wf_project.read_files(main_py_dir) before replacing app files to parse/validate import contents and raise on invalid files.
Structured Error Responses
src/writer/serve.py
Changes import/export error payloads to structured JSON objects (e.g., {"summary": "...", "details": "..."}) for 403/400/413 and ValueError paths; control flow unchanged but response shapes altered.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Client(UI)
  participant API as Server(API)
  participant App as AppRunner
  participant WF as wf_project

  UI->>API: POST /import (zip)
  API->>App: import_zip(stream)
  App->>WF: read_files(main_py_dir)  -- validate files
  alt validation fails
    WF-->>App: raises ValueError (details)
    App-->>API: return 400 {"summary": "Invalid archive contents", "details": "..."}
    API-->>UI: 400 JSON error
    UI->>UI: populate importErrorSummary/importErrorDetails
  else validation passes
    App-->>App: replace files
    App-->>API: 200 success
    API-->>UI: 200 success
    UI->>UI: hide modal, show success toast
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibbled through zips with careful pace,

Found stray crumbs of malformed trace,
I sniffed and validated every file,
Brought tidy errors with a gentle smile,
Hooray — imports now hop in grace!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 'fix: don't import broken agents' is directly related to the main objective of preventing import of broken agents with malformed files.
Linked Issues check ✅ Passed The changes implement validation of imported files before replacing app contents and structured error reporting to help identify malformed files during import.
Out of Scope Changes check ✅ Passed All changes directly support the objective of detecting and preventing import of agents with malformed JSONL files through validation and error handling.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch vlad/AB-966

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
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/writer/serve.py (1)

272-287: ⚠️ Potential issue | 🟠 Major

Ensure temp file cleanup on failed imports.

If app_runner.import_zip raises, the temporary file is not removed. Repeated failures can leak disk space.

🧹 Suggested fix
-        try:
-            with tempfile.NamedTemporaryFile(delete=False) as tmp:
+        tmp_path = None
+        try:
+            with tempfile.NamedTemporaryFile(delete=False) as tmp:
                 # Stream file to disk to avoid memory issues
                 size = 0
                 while chunk := await file.read(8192):
                     size += len(chunk)
                     if size > MAX_FILE_SIZE:
                         tmp.close()
                         os.unlink(tmp.name)
                         raise HTTPException(status_code=413, detail={"summary": f"File too large. Max file size: {MAX_FILE_SIZE}"})
                     tmp.write(chunk)
                 tmp_path = tmp.name
             await app_runner.import_zip(tmp_path)
-            os.remove(tmp_path)
+        finally:
+            if tmp_path and os.path.exists(tmp_path):
+                os.remove(tmp_path)
         except ValueError:
             raise HTTPException(status_code=400, detail={"summary": "Invalid archive contents", "details": traceback.format_exc()})
🤖 Fix all issues with AI agents
In `@src/ui/src/builder/BuilderHeaderMoreDropdown.vue`:
- Around line 85-86: importErrorSummary and importErrorDetails are retained
between modal opens causing stale errors to show; reset them to empty strings
before starting a new import attempt and when cancelling/closing the modal.
Locate the refs importErrorSummary and importErrorDetails in
BuilderHeaderMoreDropdown.vue and (a) clear both (set to "") at the start of the
import handler (before kicking off the import) and (b) clear them in the
cancel/close handler for the modal; optionally extract this into a small helper
like resetImportErrors() and call it from both the import-start and cancel/close
methods to avoid duplication.

In `@src/writer/serve.py`:
- Around line 286-287: The except block that currently reads "except
ValueError:" and raises HTTPException should preserve the original exception
context; change it to "except ValueError as e:" and re-raise the HTTPException
using exception chaining ("raise HTTPException(...) from e") so the original
ValueError is linked into the traceback while keeping the existing detail
payload (traceback.format_exc()) in the HTTPException.

@pullrequest
Copy link

pullrequest bot commented Feb 4, 2026

HackerOne Code Security Review

🟢 Scan Complete: 4 Issue(s)
🟢 Validation Complete: The engineer who manually reviewed the code found one or more things that may need attention.
🟠 Issue(s) Resolved: 0 / 1

*We want to surface issues only when necessary and actionable. If we didn't get something right, or if there's more context we should take into account, reply to the comment so we'll know for the future.

Here's how the code changes were interpreted and info about the tools used for scanning.

📖 Summary of Changes The changes focus on enhancing error handling and import processes across multiple components. Modifications include adding detailed error reporting in the UI, improving file import validation in the project runner, and refining the API endpoint's error response mechanism. These updates aim to provide more comprehensive error information during import operations.
File Summary
src/ui/src/builder/BuilderHeaderMoreDropdown.vue The changes include adding error handling for import operations, introducing new refs for error summary and details, and expanding the modal with a detailed error display section with styling for import error messages.
src/writer/app_runner.py The file contains a minor change in the import_zip method, where a new line wf_project.read_files(main_py_dir) is added before copying files to validate the imported archive's contents before synchronization.
src/writer/serve.py The changes include modifying the error handling for the /api/import endpoint, specifically updating the error response to use a dictionary with summary and optional details keys, and adding a traceback to the error response when an invalid archive is encountered.
ℹ️ Issues Detected

NOTE: These may not require action!

Below are unvalidated results from the Analysis Tools that ran during the latest scan for transparency. We investigate each of these for accuracy and relevance before surfacing them as a potential problem.

How will I know if something is a problem?
When validation completes, any concerns that warrant attention prior to merge will be posted as inline comments. These will show up in 2 ways:

  • Expert review (most cases): Issues will be posted by experts who manually reviewed and validated them. These are real HackerOne engineers (not bots) reviewing through an integrated IDE-like tool. You can communicate with them like any other reviewer. They'll stay assigned and get notified with commit & comment updates.
  • Automatically: In cases where our validation checks have highest confidence the problem is legitimate and urgent. These will include a description of contextual reasoning why & actionable next steps.
File & Line Issue
src/writer/app_runner.py Line 1211 The import_zip function calls wf_project.read_files() on user-uploaded archive contents before fully validating the archive structure. While this is intended as a validation step, if read_files() performs any code execution, file writes, or other side effects on untrusted input, this could be exploited. The archive contents at main_py_dir are extracted from a user-provided ZIP file and should be treated as untrusted. Consider ensuring that read_files() is safe to call on untrusted data, or perform additional validation before calling it.
src/ui/src/builder/BuilderHeaderMoreDropdown.vue Line 44 The importErrorDetails variable is rendered inside a <pre> tag without sanitization. This value comes from parsed?.detail?.details (line 150) which is extracted from an API response. If the backend includes unsanitized stack traces, error details, or user-controlled content, this could potentially lead to XSS. While Vue's {{ }} interpolation provides default escaping, rendering potentially sensitive error details (like stack traces) to end users may also leak implementation details that could aid attackers.
src/writer/serve.py Line 287 The error handler now includes traceback.format_exc() in the HTTP response detail, which exposes full stack traces to clients. This can leak sensitive information about the application's internal structure, file paths, library versions, and potentially security-relevant implementation details. Stack traces should be logged server-side but not returned to clients in production.
src/ui/src/builder/BuilderHeaderMoreDropdown.vue Line 35 The importErrorSummary variable is rendered directly in the template without sanitization. This value comes from parsed?.detail?.summary (line 149) which is extracted from an API response. If the backend returns unsanitized user input or malicious content in the error summary, it could lead to XSS. While Vue typically escapes interpolated content by default with {{ }}, the data flow should be verified to ensure no v-html or other unsafe rendering is used elsewhere, and the backend should sanitize error messages.
🧰 Analysis tools

⏱️ Latest scan covered changes up to commit 5ec2d9b (latest)

@pullrequest
Copy link

pullrequest bot commented Feb 4, 2026

HackerOne Code Security Review

🟢 Scan Complete: 4 Issue(s)
🟠 Validation Complete: One or more Issues looked potentially actionable, so this was escalated to our network of engineers for manual review. Once this is complete you'll see an update posted.

Here's how the code changes were interpreted and info about the tools used for scanning.

📖 Summary of Changes The changes focus on enhancing error handling and reporting for the import process across multiple components. Improvements include adding detailed error references in the UI, implementing file parsing before import in the backend, and expanding error response structures to provide more comprehensive information when file uploads or processing encounters issues.
File Summary
src/ui/src/builder/BuilderHeaderMoreDropdown.vue Added error handling for import process with new refs for error summary and details, and introduced a styled error display section in the modal with collapsible error details.
src/writer/app_runner.py The file contains a minor change in the import_zip method. A new line was added to parse files before importing to ensure there are no errors when reading the files from the imported archive.
src/writer/serve.py The changes include modifications to the import_zip endpoint, specifically adding more detailed error responses with summary and optional details fields for HTTP exceptions. The import_zip function now provides more structured error information when file upload or processing fails.
ℹ️ Issues Detected

NOTE: These may not require action!

Below are unvalidated results from the Analysis Tools that ran during the latest scan for transparency. We investigate each of these for accuracy and relevance before surfacing them as a potential problem.

How will I know if something is a problem?
When validation completes, any concerns that warrant attention prior to merge will be posted as inline comments. These will show up in 2 ways:

  • Expert review (most cases): Issues will be posted by experts who manually reviewed and validated them. These are real HackerOne engineers (not bots) reviewing through an integrated IDE-like tool. You can communicate with them like any other reviewer. They'll stay assigned and get notified with commit & comment updates.
  • Automatically: In cases where our validation checks have highest confidence the problem is legitimate and urgent. These will include a description of contextual reasoning why & actionable next steps.
File & Line Issue
src/ui/src/builder/BuilderHeaderMoreDropdown.vue Line 35 The importErrorSummary variable is rendered directly in the template without sanitization using Vue's text interpolation ({{ importErrorSummary }}). This value comes from parsed?.detail?.summary (line 145) which is extracted from an API response. If the backend returns unsanitized user input or malicious content in the error summary, it could lead to XSS. While Vue's default text interpolation does provide some protection, the data originates from an external API response that may not be fully trusted. Consider explicitly sanitizing or validating this error message content before display.
src/writer/serve.py Line 287 The error handler now exposes the full stack trace via traceback.format_exc() in the HTTP response detail. This can leak sensitive information about the application's internal structure, file paths, library versions, and code logic to clients. Stack traces should be logged server-side but not returned in API responses to untrusted clients.
src/writer/app_runner.py Line 1211 The import_zip function calls wf_project.read_files() on user-uploaded ZIP content without proper validation. While this is intended to parse and validate the files, if read_files() performs any code execution, deserialization of untrusted data, or has vulnerabilities in its parsing logic, this could lead to arbitrary code execution or denial of service. The ZIP file contents are extracted from user input and processed before being validated, which could allow malicious archives to exploit parsing vulnerabilities. Consider adding explicit validation of file types, sizes, and content before parsing, and ensure wf_project.read_files() is hardened against malicious input.
src/ui/src/builder/BuilderHeaderMoreDropdown.vue Line 44 The importErrorDetails variable is rendered directly in a <pre> tag without sanitization using Vue's text interpolation ({{ importErrorDetails }}). This value comes from parsed?.detail?.details (line 146) which is extracted from an API response. Stack traces or error details from the backend could potentially contain unsanitized user input or malicious content. If an attacker can influence the error details returned by the import API, this could lead to XSS. Consider sanitizing or validating this content before rendering, especially since it's displayed in a collapsible details section that users might expand.
🧰 Analysis tools

⏱️ Latest scan covered changes up to commit 3bbbafa (latest)

@pullrequest
Copy link

pullrequest bot commented Feb 4, 2026

✅ Graham C reviewed all the included code changes and associated automation findings and determined that there were no immediately actionable security flaws. Note that they will continue to be notified of any new commits or comments and follow up as needed throughout the duration of this pull request's lifecycle.

Image of Graham C Graham C


Reviewed with ❤️ by PullRequest

Copy link
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

🤖 Fix all issues with AI agents
In `@src/writer/serve.py`:
- Around line 278-281: The error currently reports MAX_FILE_SIZE as raw bytes;
update the HTTPException detail in the branch where size > MAX_FILE_SIZE to
include a human-readable size string instead of the raw number by converting
MAX_FILE_SIZE (and/or size) to a readable format (e.g., "200 MB") before
building the detail message; add or call a small helper like
human_readable_size(value) and use it in the f-string passed to HTTPException
(referencing MAX_FILE_SIZE, tmp, and the existing exception construction) so the
summary reads "File too large. Max file size: 200 MB".
🧹 Nitpick comments (2)
src/ui/src/builder/BuilderHeaderMoreDropdown.vue (1)

147-152: Consider more defensive JSON parsing.

If the server returns a non-JSON error response (e.g., a 500 with an HTML body), response.json() will throw, causing the catch block to show a generic toast while the modal error block remains empty. This is acceptable fallback behavior, but you could make it more explicit:

💡 Optional: More defensive parsing
 		if (!response.ok) {
-			const parsed = await response.json();
-			importErrorSummary.value = parsed?.detail?.summary;
-			importErrorDetails.value = parsed?.detail?.details;
+			try {
+				const parsed = await response.json();
+				importErrorSummary.value = parsed?.detail?.summary ?? "Unknown error";
+				importErrorDetails.value = parsed?.detail?.details;
+			} catch {
+				importErrorSummary.value = "Failed to parse server error response";
+			}
 			throw new Error("Failed to connect to import API");
 		}
src/writer/serve.py (1)

250-253: Note: export endpoint retains plain string error format.

The export_zip endpoint at line 253 still uses detail="Invalid mode." while import_zip now uses structured errors. This is acceptable since the export flow doesn't use the same error display modal, but consider aligning for consistency if error handling is unified in the future.

@pullrequest
Copy link

pullrequest bot commented Feb 4, 2026

Francisco P has submitted feedback.

Image of Francisco P Francisco P


Reviewed with ❤️ by PullRequest

@pullrequest
Copy link

pullrequest bot commented Feb 4, 2026

Francisco P has submitted feedback.

Image of Francisco P Francisco P


Reviewed with ❤️ by PullRequest

@pullrequest
Copy link

pullrequest bot commented Feb 4, 2026

Francisco P has submitted feedback.

Image of Francisco P Francisco P


Reviewed with ❤️ by PullRequest

except ValueError:
raise HTTPException(status_code=400, detail="Invalid upload.")
except ValueError as e:
raise HTTPException(status_code=400, detail={"summary": "Invalid archive contents", "details": traceback.format_exc()}) from e
Copy link

Choose a reason for hiding this comment

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

The full stack trace is included in the HTTP response which can potentially expose sensitive system details.

Risk
Exposing stack traces in API responses can:

  • Aid attackers in understanding internal code structure
  • Reveal sensitive configuration or environment details
  • Increase the effectiveness of targeted attacks and vulnerability exploitation

References:
CWE-532: Insertion of Sensitive Information into Log File
[OWASP Error Handling Guidelines]

Recommendation:

  • Do not include stack traces or raw exception details in client-facing API responses.
🔺 Vulnerability (Error)

Image of Francisco P Francisco P

@UladzislauK-Writer UladzislauK-Writer merged commit da82279 into dev Feb 4, 2026
17 checks passed
@UladzislauK-Writer UladzislauK-Writer deleted the vlad/AB-966 branch February 4, 2026 14:18
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.

1 participant