Skip to content

Improve provider compatibility, browser automation workflow, and configuration handling - #741

Open
L0W-LIGHT wants to merge 1 commit into
browser-use:mainfrom
L0W-LIGHT:feat/careercopilot-improvements
Open

Improve provider compatibility, browser automation workflow, and configuration handling#741
L0W-LIGHT wants to merge 1 commit into
browser-use:mainfrom
L0W-LIGHT:feat/careercopilot-improvements

Conversation

@L0W-LIGHT

@L0W-LIGHT L0W-LIGHT commented Aug 6, 2026

Copy link
Copy Markdown

Summary

This PR contains several improvements made while testing and extending the project.

Highlights

  • Improved LLM provider compatibility
  • Better Docker configuration
  • Browser automation improvements
  • Configuration persistence improvements
  • Better setup validation
  • UI and workflow refinements

These changes were tested using Docker with Gemini and Ollama providers.


Summary by cubic

Migrates the Web UI to streamlit and adds a guided job-application workflow with persistent profile and application history. Improves LLM provider compatibility, browser automation reliability, and Docker configuration, with new security and deployment guides.

  • New Features

    • New streamlit app with pages for Profile, Browser Settings, Job Application, and Application History.
    • Persistent config and data via StreamlitManager and JSON files under data/.
    • Async browser start and external browser path support in CustomBrowser; controller expanded with job tools.
    • Deployment and security docs added (STREAMLIT_DEPLOYMENT.md, SECURITY_GUIDE.md), plus .streamlit/config.toml.
    • Docker updates: expose 8501, start streamlit_app.py via supervisord; runner script run_streamlit.py.
  • Migration

    • Install deps: pip install -r requirements.txt (replaces gradio with streamlit, adds python-dotenv, bumps langchain-ibm).
    • Copy env.example to .env and set keys (e.g., OPENAI_API_KEY, LLM_PROVIDER, LLM_MODEL).
    • Start locally: python run_streamlit.py or streamlit run streamlit_app.py --server.port 8501.
    • For Docker, use updated docker-compose.yml (port 8501 exposed).

Written for commit a187ed3. Summary will update on new commits.

Review in cubic

@CLAassistant

CLAassistant commented Aug 6, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@cubic-dev-ai cubic-dev-ai 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.

40 issues found across 32 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/webui/components/profile_settings_tab.py">

<violation number="1" location="src/webui/components/profile_settings_tab.py:136">
P1: Saving any profile edit without selecting a replacement resume erases `documents.resume_path`, even though the form only displays the existing resume and the application workflow depends on that path. Initialize `resume_path` from the existing profile and overwrite it only when a new file is uploaded.</violation>

<violation number="2" location="src/webui/components/profile_settings_tab.py:140">
P1: Uploading a resume makes Save Profile fail because `gr.File(type="filepath")` passes a path string while this handler expects an object with `.name`. Use `resume_file` directly for `os.path.basename` and `shutil.copy2`.</violation>

<violation number="3" location="src/webui/components/profile_settings_tab.py:264">
P0: The Gradio WebUI crashes during startup with the repository's existing profile because `current_position` is a string in the shared profile schema, not the dict this tab expects. Normalize or migrate the shared profile before dereferencing it so switching between the Streamlit and Gradio UIs remains compatible.</violation>
</file>

<file name="src/webui/streamlit_components/job_application.py">

<violation number="1" location="src/webui/streamlit_components/job_application.py:44">
P1: Selecting Gemini, Mistral, or another non-Ollama provider can use an unrelated OpenAI/Anthropic key whenever either generic key is configured, causing provider authentication failures. Let `get_llm_model` resolve the selected provider key or use `f"{provider.upper()}_API_KEY"` here.</violation>

<violation number="2" location="src/webui/streamlit_components/job_application.py:364">
P1: A non-LinkedIn host can pass validation and receive the credential-bearing automation task, creating a phishing/credential-exfiltration path. Parse the URL and require an HTTPS `linkedin.com` hostname (including only intended subdomains) before launching the agent.</violation>

<violation number="3" location="src/webui/streamlit_components/job_application.py:385">
P0: The user's LinkedIn password is sent as ordinary LLM prompt text and can also be emitted in the task preview log, despite the UI saying credentials are only used for the session. Keep the password out of the task and logs and provide it through a protected browser credential mechanism instead.</violation>

<violation number="4" location="src/webui/streamlit_components/job_application.py:390">
P1: Only the first supplied search URL is automated while the UI claims all search URLs will be processed, so additional searches are silently skipped. Include every validated URL in the task or loop over the URLs in the active runner.</violation>

<violation number="5" location="src/webui/streamlit_components/job_application.py:461">
P1: The Stop button cannot stop an in-progress automation: the synchronous `asyncio.run` call prevents the stop handler from running during the job loop, and flipping session state does not stop the agent or browser. Run the agent as a managed background task and invoke its stop/cleanup path when the user requests cancellation.</violation>
</file>

<file name="src/browser/custom_browser.py">

<violation number="1" location="src/browser/custom_browser.py:44">
P1: Creating a context launches a second Playwright/browser instance and leaves the context's actual browser running after `CustomBrowser.close()`; use the base `playwright`/`playwright_browser` fields or override `get_playwright_browser()` and close those same objects.</violation>

<violation number="2" location="src/browser/custom_browser.py:47">
P1: Configured CDP/WSS browser connections are ignored, so remote-browser workflows launch a local browser instead of connecting to the endpoint; preserve the base priority by checking `cdp_url` and `wss_url` before selecting the external or built-in launcher.</violation>

<violation number="3" location="src/browser/custom_browser.py:61">
P2: After closing, subsequent contexts can reuse the closed browser, and a close error can leave Playwright running; clear both handles in a `finally` block and shut down each resource independently.</violation>
</file>

<file name="src/webui/streamlit_components/config_manager.py">

<violation number="1" location="src/webui/streamlit_components/config_manager.py:120">
P2: Imported profile and browser settings disappear when the Streamlit session is recreated because these assignments update only session state. Persist each imported section through `manager.save_settings` before rerunning.</violation>

<violation number="2" location="src/webui/streamlit_components/config_manager.py:123">
P2: An exported configuration cannot round-trip its LLM settings because this import block ignores the `llm_config` that the same page exports. Restore those fields into the page/application configuration while continuing to source secrets from the environment.</violation>

<violation number="3" location="src/webui/streamlit_components/config_manager.py:214">
P1: Changing the LLM fields here does not change the connection test or the agent used for job applications: both paths read environment variables instead of the widget values. Pass this selected configuration through the manager/application path so the page’s controls are authoritative.</violation>

<violation number="4" location="src/webui/streamlit_components/config_manager.py:237">
P2: `Check Environment` reports the documented/tested Ollama setup as misconfigured when no OpenAI key is present, and it checks Anthropic/Azure setups against the wrong key. Derive required credentials from the active provider and exempt Ollama.</violation>
</file>

<file name="src/webui/components/job_application_tab.py">

<violation number="1" location="src/webui/components/job_application_tab.py:8">
P1: The Gradio UI fails during startup with `NameError: gr is not defined`; restore the Gradio import instead of commenting it out.</violation>

<violation number="2" location="src/webui/components/job_application_tab.py:193">
P1: A crafted non-LinkedIn URL can pass validation and receive the user's LinkedIn credentials; validate the parsed HTTPS hostname as exactly `linkedin.com` or a subdomain before starting the browser agent.</violation>

<violation number="3" location="src/webui/components/job_application_tab.py:210">
P1: Ollama runs configured through Docker cannot connect to Ollama because the function always passes the OpenAI/Anthropic endpoint for every provider; let `llm_provider.get_llm_model()` resolve provider-specific endpoints and credentials.</violation>

<violation number="4" location="src/webui/components/job_application_tab.py:350">
P1: Applications can be submitted with inaccurate personal and eligibility data because these fields are hard-coded rather than read from the saved profile; use profile values and stop or ask for input when required values are missing.</violation>

<violation number="5" location="src/webui/components/job_application_tab.py:357">
P1: The agent never receives the profile-driven application workflow, so applications are not instructed to load or log the saved profile and the role/location overrides have no effect; pass the full workflow prompt plus the per-run values to the agent.</violation>

<violation number="6" location="src/webui/components/job_application_tab.py:381">
P1: A terminal agent response containing the word `success` can be reported as a submitted application even when no application was sent; base the result on a verified confirmation and logged `submitted` record instead.</violation>
</file>

<file name="data/profile/profile.json">

<violation number="1" location="data/profile/profile.json:5">
P1: This commits real personal data to the repository: a private email address, phone number (8067025056), residential street address (2591 piedmont rd), city/state/ZIP (Atlanta, GA 30324), and a named resume path. `data/applications/applications.json` similarly exposes real job-search activity. This is a privacy/security leak — user PII should not be version-controlled, and since `.gitignore` does not exclude `data/profile/` or `data/applications/`, these files will be committed and stay in git history. Please remove these runtime state files from the repo and add them to `.gitignore` (keep only an empty template/placeholder or load them from env/config instead).</violation>
</file>

<file name="src/webui/components/browser_use_agent_tab.py">

<violation number="1" location="src/webui/components/browser_use_agent_tab.py:1074">
P1: Clicking the displayed “Submit Response” button cannot submit assistance while the original `run_button` event is still waiting in `run_agent_task`. Retain `trigger_mode="multiple"` for this listener (or route responses through a separate listener), since Gradio’s default `once` mode rejects the required second submission.</violation>
</file>

<file name="src/controller/custom_controller.py">

<violation number="1" location="src/controller/custom_controller.py:262">
P2: Deleting an application and then logging another can reuse an existing ID and cause the history UI to delete multiple records; derive the next ID from the maximum existing ID rather than the list length.</violation>

<violation number="2" location="src/controller/custom_controller.py:324">
P2: Application analytics omit existing capitalized statuses and can report zero submitted applications; normalize the stored status before checking `status_stats` (and consider mapping legacy `Success` values).</violation>

<violation number="3" location="src/controller/custom_controller.py:363">
P2: The registered extraction tool reports success without extracting anything, so an agent that calls it cannot obtain the listings needed for the advertised workflow; implement the extraction or remove/rename this placeholder action.</violation>

<violation number="4" location="src/controller/custom_controller.py:416">
P1: Visa-status questions are filled with the user's work-authorization value, which can submit an incorrect answer on application forms; map `visa` to `preferences.visa_status` and keep authorization questions on `work_authorization`.</violation>
</file>

<file name="streamlit_app.py">

<violation number="1" location="streamlit_app.py:320">
P1: The profile edited in My Profile is not the profile used by the automation: this entry point points the manager at `tmp/webui_settings`, but `CustomController.get_profile()` reads `data/profile/profile.json`, so applications can run with stale or empty profile data after a successful save. Point the manager at the controller's profile store or pass the manager profile into the controller.</violation>

<violation number="2" location="streamlit_app.py:425">
P2: Browser Setup changes do not affect the browser used for applications: the apply page hardcodes Chromium, visible mode, and its dimensions instead of consuming `manager.browser_config`. Wiring the apply flow to the saved configuration would make these controls functional; currently they only change session/display state.</violation>
</file>

<file name="src/webui/streamlit_components/profile_settings.py">

<violation number="1" location="src/webui/streamlit_components/profile_settings.py:145">
P2: These select boxes always open at the first option (hardcoded index=0) and never reflect the saved profile, even though the sibling text/number fields on the same page load their saved values. So reopening the page and clicking Save silently resets every selectable preference (education level, work authorization, availability, remote preference, EEO fields, etc.) back to defaults, silently corrupting the stored profile. Compute the index from the saved value (e.g. options.index(saved_value) with a fallback to 0) for each select box, mirroring how the text inputs are initialized.</violation>

<violation number="2" location="src/webui/streamlit_components/profile_settings.py:300">
P2: A crafted upload filename can write outside `data/documents` because `uploaded_resume.name` is used without path sanitization. Normalize it with `os.path.basename` before constructing `resume_path`.</violation>

<violation number="3" location="src/webui/streamlit_components/profile_settings.py:355">
P1: After a resume is uploaded, any later save without selecting the file again clears `documents.resume_path` and `documents.resume_name`, leaving the application workflow without a resume. Preserve the existing documents fields and overlay the new upload only when present.</violation>
</file>

<file name="src/webui/streamlit_components/application_history.py">

<violation number="1" location="src/webui/streamlit_components/application_history.py:35">
P2: The history statistics and status filter do not recognize the statuses already persisted in `data/applications/applications.json` (`Submitted`/`Success`). Normalize status values and map the legacy `Success` value to the page’s `submitted` status before counting and filtering.</violation>

<violation number="2" location="src/webui/streamlit_components/application_history.py:164">
P2: Clear All History can never complete through the UI: checking the confirmation box triggers a rerun in which the button is no longer pressed. Render the confirmation checkbox outside the button branch (or persist the button click in session state) and then require both values before deleting.</violation>
</file>

<file name="SECURITY_GUIDE.md">

<violation number="1" location="SECURITY_GUIDE.md:43">
P2: The guide claims .env.local, .env.production, *.pem and credentials.json are protected from git, but .gitignore excludes only .env and .streamlit/secrets.toml, so these files are not actually ignored (verified via git check-ignore exit 1). A user following the guide could unknowingly commit them; either add these patterns to .gitignore or correct the documentation.</violation>
</file>

<file name="src/webui/components/application_history_tab.py">

<violation number="1" location="src/webui/components/application_history_tab.py:119">
P1: Application history can lose updates when automation logs an application while this delete operation runs because both paths use unsynchronized read-modify-write file updates. Use a shared lock or a single atomic storage/update helper for logging, deletion, and clearing.</violation>

<violation number="2" location="src/webui/components/application_history_tab.py:247">
P2: After a successful delete, the table no longer reflects the selected status filter because the handler hard-codes `"All"`. Pass the current `status_filter` into this event and refresh with that value.</violation>
</file>

<file name="src/webui/streamlit_manager.py">

<violation number="1" location="src/webui/streamlit_manager.py:85">
P2: Saved browser settings disappear after a new Streamlit session or restart because initialization replaces them with `{}` instead of loading `browser_config.json`. Initialize this state with `self.load_settings("browser_config.json")` so the Save Browser Settings feature persists across sessions.</violation>
</file>

<file name=".gitignore">

<violation number="1" location=".gitignore:8">
P1: Rewriting `.gitignore` dropped the `data/` entry (plus `*.pdf`, `browser_cookies.json`, `cookies.json`, `AgentHistory.json`, `.venv`/`venv/`). This is a privacy regression: `data/` is where the app persists profiles, application history and resumes (the new `env.example` even documents `PROFILE_DIR=data` / `APPLICATIONS_DIR=data`), and it is already tracked in git — `data/profile/documents/Dinesh_Satram_Resume_DS_.pdf`, `data/profile/profile.json` and `data/applications/applications.json` are currently committed. Browser session cookies and personal resumes will now also be candidates for accidental commits. Please restore the ignored patterns for `data/`, cookie/history files and virtualenvs so user PII and session data stay out of version control. The file also documents those intentionally-removed patterns (e.g. the previous `data/`, `*.pdf`, `workflow` lines) as absent now.</violation>
</file>

<file name="requirements.txt">

<violation number="1" location="requirements.txt:3">
P2: Removing `gradio` here breaks the still-documented primary entry point. `webui.py` (which README still instructs users to run via `python webui.py --ip 127.0.0.1 --port 7788`) imports `create_ui` from `src/webui/interface.py`, and `interface.py` plus `src/webui/components/*` all do `import gradio as gr`. With gradio dropped from requirements, that documented app path fails with `ModuleNotFoundError: No module named 'gradio'`. If the migration to Streamlit is intentional and the Gradio UI is being retired, `webui.py`/`src/webui` should be removed or explicitly deprecated in the README; otherwise keep `gradio` in requirements alongside Streamlit so both UIs remain runnable.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

with gr.Row():
current_job_title = gr.Textbox(
label="Job Title",
value=current_position.get("job_title", ""),

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P0: The Gradio WebUI crashes during startup with the repository's existing profile because current_position is a string in the shared profile schema, not the dict this tab expects. Normalize or migrate the shared profile before dereferencing it so switching between the Streamlit and Gradio UIs remains compatible.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/components/profile_settings_tab.py, line 264:

<comment>The Gradio WebUI crashes during startup with the repository's existing profile because `current_position` is a string in the shared profile schema, not the dict this tab expects. Normalize or migrate the shared profile before dereferencing it so switching between the Streamlit and Gradio UIs remains compatible.</comment>

<file context>
@@ -0,0 +1,687 @@
+        with gr.Row():
+            current_job_title = gr.Textbox(
+                label="Job Title",
+                value=current_position.get("job_title", ""),
+                placeholder="Software Engineer, Data Scientist, etc."
+            )
</file context>
Fix with cubic


CREDENTIALS (CRITICAL - USE EXACTLY):
- LinkedIn Email: {linkedin_email}
- LinkedIn Password: {linkedin_password}

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P0: The user's LinkedIn password is sent as ordinary LLM prompt text and can also be emitted in the task preview log, despite the UI saying credentials are only used for the session. Keep the password out of the task and logs and provide it through a protected browser credential mechanism instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/streamlit_components/job_application.py, line 385:

<comment>The user's LinkedIn password is sent as ordinary LLM prompt text and can also be emitted in the task preview log, despite the UI saying credentials are only used for the session. Keep the password out of the task and logs and provide it through a protected browser credential mechanism instead.</comment>

<file context>
@@ -0,0 +1,1240 @@
+                
+                CREDENTIALS (CRITICAL - USE EXACTLY):
+                - LinkedIn Email: {linkedin_email}
+                - LinkedIn Password: {linkedin_password}
+                
+                TASK STEPS:
</file context>
Fix with cubic

if resume_file:
resume_dir = "./data/profile/documents"
os.makedirs(resume_dir, exist_ok=True)
resume_filename = os.path.basename(resume_file.name)

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P1: Uploading a resume makes Save Profile fail because gr.File(type="filepath") passes a path string while this handler expects an object with .name. Use resume_file directly for os.path.basename and shutil.copy2.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/components/profile_settings_tab.py, line 140:

<comment>Uploading a resume makes Save Profile fail because `gr.File(type="filepath")` passes a path string while this handler expects an object with `.name`. Use `resume_file` directly for `os.path.basename` and `shutil.copy2`.</comment>

<file context>
@@ -0,0 +1,687 @@
+        if resume_file:
+            resume_dir = "./data/profile/documents"
+            os.makedirs(resume_dir, exist_ok=True)
+            resume_filename = os.path.basename(resume_file.name)
+            resume_path = os.path.join(resume_dir, resume_filename)
+            
</file context>
Fix with cubic


try:
# Create Playwright instance and launch browser
self._playwright = await async_playwright().start()

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P1: Creating a context launches a second Playwright/browser instance and leaves the context's actual browser running after CustomBrowser.close(); use the base playwright/playwright_browser fields or override get_playwright_browser() and close those same objects.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/browser/custom_browser.py, line 44:

<comment>Creating a context launches a second Playwright/browser instance and leaves the context's actual browser running after `CustomBrowser.close()`; use the base `playwright`/`playwright_browser` fields or override `get_playwright_browser()` and close those same objects.</comment>

<file context>
@@ -32,14 +32,103 @@
+        
+        try:
+            # Create Playwright instance and launch browser
+            self._playwright = await async_playwright().start()
+            
+            # Check if external browser path is specified
</file context>
Fix with cubic

if st.button("🔍 Test LLM Connection"):
try:
from src.utils import llm_provider
provider = os.getenv("LLM_PROVIDER", "openai")

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P1: Changing the LLM fields here does not change the connection test or the agent used for job applications: both paths read environment variables instead of the widget values. Pass this selected configuration through the manager/application path so the page’s controls are authoritative.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/streamlit_components/config_manager.py, line 214:

<comment>Changing the LLM fields here does not change the connection test or the agent used for job applications: both paths read environment variables instead of the widget values. Pass this selected configuration through the manager/application path so the page’s controls are authoritative.</comment>

<file context>
@@ -0,0 +1,243 @@
+        if st.button("🔍 Test LLM Connection"):
+            try:
+                from src.utils import llm_provider
+                provider = os.getenv("LLM_PROVIDER", "openai")
+                model = os.getenv("LLM_MODEL", "gpt-4o")
+                
</file context>
Fix with cubic

if 'profile_data' not in st.session_state:
st.session_state.profile_data = self.load_settings("profile.json")
if 'browser_config' not in st.session_state:
st.session_state.browser_config = {}

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P2: Saved browser settings disappear after a new Streamlit session or restart because initialization replaces them with {} instead of loading browser_config.json. Initialize this state with self.load_settings("browser_config.json") so the Save Browser Settings feature persists across sessions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/streamlit_manager.py, line 85:

<comment>Saved browser settings disappear after a new Streamlit session or restart because initialization replaces them with `{}` instead of loading `browser_config.json`. Initialize this state with `self.load_settings("browser_config.json")` so the Save Browser Settings feature persists across sessions.</comment>

<file context>
@@ -0,0 +1,211 @@
+        if 'profile_data' not in st.session_state:
+            st.session_state.profile_data = self.load_settings("profile.json")
+        if 'browser_config' not in st.session_state:
+            st.session_state.browser_config = {}
+        if 'application_status' not in st.session_state:
+            st.session_state.application_status = "idle"
</file context>
Fix with cubic


with col14:
if st.button("🔐 Check Environment"):
required_vars = ["OPENAI_API_KEY", "LLM_PROVIDER", "LLM_MODEL"]

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P2: Check Environment reports the documented/tested Ollama setup as misconfigured when no OpenAI key is present, and it checks Anthropic/Azure setups against the wrong key. Derive required credentials from the active provider and exempt Ollama.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/streamlit_components/config_manager.py, line 237:

<comment>`Check Environment` reports the documented/tested Ollama setup as misconfigured when no OpenAI key is present, and it checks Anthropic/Azure setups against the wrong key. Derive required credentials from the active provider and exempt Ollama.</comment>

<file context>
@@ -0,0 +1,243 @@
+    
+    with col14:
+        if st.button("🔐 Check Environment"):
+            required_vars = ["OPENAI_API_KEY", "LLM_PROVIDER", "LLM_MODEL"]
+            missing_vars = [var for var in required_vars if not os.getenv(var)]
+            
</file context>
Fix with cubic

if uploaded_resume:
# Save uploaded file
os.makedirs("data/documents", exist_ok=True)
resume_path = f"data/documents/{uploaded_resume.name}"

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P2: A crafted upload filename can write outside data/documents because uploaded_resume.name is used without path sanitization. Normalize it with os.path.basename before constructing resume_path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/streamlit_components/profile_settings.py, line 300:

<comment>A crafted upload filename can write outside `data/documents` because `uploaded_resume.name` is used without path sanitization. Normalize it with `os.path.basename` before constructing `resume_path`.</comment>

<file context>
@@ -0,0 +1,378 @@
+    if uploaded_resume:
+        # Save uploaded file
+        os.makedirs("data/documents", exist_ok=True)
+        resume_path = f"data/documents/{uploaded_resume.name}"
+        with open(resume_path, "wb") as f:
+            f.write(uploaded_resume.getbuffer())
</file context>
Suggested change
resume_path = f"data/documents/{uploaded_resume.name}"
resume_path = os.path.join("data/documents", os.path.basename(uploaded_resume.name))
Fix with cubic

Comment thread requirements.txt
browser-use==0.1.48
pyperclip==1.9.0
gradio==5.27.0
streamlit==1.31.0

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P2: Removing gradio here breaks the still-documented primary entry point. webui.py (which README still instructs users to run via python webui.py --ip 127.0.0.1 --port 7788) imports create_ui from src/webui/interface.py, and interface.py plus src/webui/components/* all do import gradio as gr. With gradio dropped from requirements, that documented app path fails with ModuleNotFoundError: No module named 'gradio'. If the migration to Streamlit is intentional and the Gradio UI is being retired, webui.py/src/webui should be removed or explicitly deprecated in the README; otherwise keep gradio in requirements alongside Streamlit so both UIs remain runnable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At requirements.txt, line 3:

<comment>Removing `gradio` here breaks the still-documented primary entry point. `webui.py` (which README still instructs users to run via `python webui.py --ip 127.0.0.1 --port 7788`) imports `create_ui` from `src/webui/interface.py`, and `interface.py` plus `src/webui/components/*` all do `import gradio as gr`. With gradio dropped from requirements, that documented app path fails with `ModuleNotFoundError: No module named 'gradio'`. If the migration to Streamlit is intentional and the Gradio UI is being retired, `webui.py`/`src/webui` should be removed or explicitly deprecated in the README; otherwise keep `gradio` in requirements alongside Streamlit so both UIs remain runnable.</comment>

<file context>
@@ -1,10 +1,11 @@
 browser-use==0.1.48
 pyperclip==1.9.0
-gradio==5.27.0
+streamlit==1.31.0
 json-repair
 langchain-mistralai==0.2.4
</file context>
Suggested change
streamlit==1.31.0
streamlit==1.31.0
gradio==5.27.0
Fix with cubic

education_level = st.selectbox(
"Highest Education Level",
["High School", "Associate's Degree", "Bachelor's Degree", "Master's Degree", "PhD", "Other"],
index=0,

@cubic-dev-ai cubic-dev-ai Bot Aug 6, 2026

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.

P2: These select boxes always open at the first option (hardcoded index=0) and never reflect the saved profile, even though the sibling text/number fields on the same page load their saved values. So reopening the page and clicking Save silently resets every selectable preference (education level, work authorization, availability, remote preference, EEO fields, etc.) back to defaults, silently corrupting the stored profile. Compute the index from the saved value (e.g. options.index(saved_value) with a fallback to 0) for each select box, mirroring how the text inputs are initialized.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/streamlit_components/profile_settings.py, line 145:

<comment>These select boxes always open at the first option (hardcoded index=0) and never reflect the saved profile, even though the sibling text/number fields on the same page load their saved values. So reopening the page and clicking Save silently resets every selectable preference (education level, work authorization, availability, remote preference, EEO fields, etc.) back to defaults, silently corrupting the stored profile. Compute the index from the saved value (e.g. options.index(saved_value) with a fallback to 0) for each select box, mirroring how the text inputs are initialized.</comment>

<file context>
@@ -0,0 +1,378 @@
+        education_level = st.selectbox(
+            "Highest Education Level",
+            ["High School", "Associate's Degree", "Bachelor's Degree", "Master's Degree", "PhD", "Other"],
+            index=0,
+            key="profile_education_level"
+        )
</file context>
Fix with cubic

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