Originally created by: theBGuy
Originally owned by: theBGuy
This change integrates Bitbucket Cloud as a fully-supported code hosting provider alongside GitHub and GitLab, bringing repository browsing, pull request and Pipelines support (read and write), publish, Insights, and extensive repository management into the desktop app. AI reviews and prompts are now aware of the hosting provider and can incorporate external context. The frontend surfaces for publish, Insights, and notifications are unified to provide consistent interactions across all supported providers.
src-tauri/src/forge/bitbucket.rs, including repo listing, pull requests (read/write), Pipelines, repository settings, and deployments.src-tauri/src/forge/http.rs), and error handling extensions (src-tauri/src/error.rs).src-tauri/src/forge/model.rs, integrating Bitbucket types and mapping API responses.src-tauri/src/forge/mod.rs) and provider capabilities.src/features/settings/AccountsSection.tsx, src/features/repository/ForgeNotReady.tsx, and related files.src/features/pulls/PrTasksSection.tsx), with add/edit/resolve/delete, progress bar, and closed-state handling.src/features/pulls/CreatePrDialog.tsx, src/features/pulls/ReviewersPopover.tsx, and related inputs.src/features/actions/RunWorkflowDialog.tsx, src/features/repo-settings/BitbucketEnvironmentsSection.tsx).src/features/repo-settings/* components.src/features/repository/PublishRepoControl.tsx, src/features/repository/PublishDialog.tsx, plus UI references).src/features/repository/insights/InsightsBoard.tsx, src/features/repository/insights/LinkOutsCard.tsx).src/features/repository/usePrNotifications.ts).src/lib/ai/prompt.ts, src/lib/ai/types.ts, src/features/pulls/useGeneratePrDescription.ts, src/features/tags/useGenerateReleaseNotes.ts).src/lib/ai/external-context.ts).src-tauri/src/forge/model.rs).src/lib/git/api.ts, src/lib/git/queries.ts, src/lib/git/types.ts).src/features/repository/SyncControls.tsx, src/features/repo-settings/DangerZone.tsx, src/lib/pulls/queries.ts).README.md, CHANGELOG.md, and main site (site/src/pages/index.astro).These changes collectively deliver first-class Bitbucket Cloud integration and enable a unified workflow for repositories and pull requests across all supported forges, both in the desktop experience and via AI-driven features.
Originally posted by: cloudflare-workers-and-pages[bot]
Deploying gitdesktop with
Cloudflare Pages
5b5109aView logs
Originally posted by: theBGuy
AI review (sonnet) · automated
This is a very large first-class provider integration (~5500 lines of Rust + significant frontend changes) that follows the established GitLab patterns closely: OS-keyring credentials, neutral model mapping, pre-mutation guards, paginated reads, post-mutation disclosure on partial failures, and a strong unit-test suite covering the mapping layer. The architecture is sound and the overall implementation is high-quality. Two genuine issues worth fixing before shipping.
Correctness
should-fix ·
bitbucket.rs·publish_repo/git credential approveseedingThe credential seed at ~line 3679 uses a hardcoded
username=x-bitbucket-api-token-auth:But the module-level doc and the
BbCredentialstype both document that Bitbucket authenticates over HTTP Basic as{atlassian_account_email}:{api_token}— the email is the username, not a special sentinel.git credential approveseeds whatever username git will subsequently emit in theAuthorization: Basic ...header, so a mismatch means the seeded credential is never matched and the immediategit push -c credential.interactive=falsewill fail for any user who does not already have a separate GCM OAuth session forbitbucket.org.The error path discloses "The Bitbucket repository was created at X, but pushing failed", so it's not silent, but the publish flow is unusable in the normal onboarding case (user connects a token, then immediately tries to publish a local repo with no pre-existing GCM session).
Fix: use
creds.emailas the username:Edge cases
should-fix ·
bitbucket.rs·rewritten_origin_urlThe HTTPS branch rewrites any
http:///https://origin URL unconditionally:A user who cloned through a corporate git proxy (
https://git-proxy.corp.example.com/ws/slug.git) would have their proxy URL silently replaced with the canonicalbitbucket.orgURL after a rename, breaking future fetches/pushes through the proxy. The function returnsNonewhen the URL isn't recognized, so the fix is to guard on the host:Nits
nit ·
bitbucket.rs·pr_tasksinitial URLEvery other paginated list endpoint in the file explicitly requests
pagelen=100. Thepr_tasksfirst-page URL is{base}/pullrequests/{number}/taskswith nopagelen, so it uses Bitbucket's server default (typically 10–20). Subsequent pages follownext, so correctness is preserved, but the first page usually requires an immediate pagination round-trip for any PR with more than ~10 tasks. Should be{base}/pullrequests/{number}/tasks?pagelen=100.nit ·
bitbucket.rs·resolve_pipelinefallback catches auth errorsThe fallback from the primary pipeline GET to the query-list endpoint catches
AppError::Bitbucket(_)broadly:Because
http_error(401, …)andhttp_error(429, …)both produceAppError::Bitbucket(…), an expired token triggers two consecutive API calls to the same expired endpoint. The error message from the second call is correct, so this is only a performance nit, but it's worth tightening the match to only a genuine "not found" status to avoid the double request on auth failures.Originally posted by: theBGuy
AI security audit (sonnet) · automated
Let me examine the key security-relevant files in this diff.Now let me look at the SSRF-relevant parts of
resolve_urlwith the paginationnexthandling, and the Tauri command registration.Let me examine more of the credential handling and theAccountsSection.tsxchanges to check if the token/email are exposed over IPC.No genuine security vulnerabilities in these changes meet the reporting thresholds.Triage of Copilot flags:
Copilot flagged
ForgeNotReady.tsx:35(stale "not yet implemented" comment) andPullRequestsPanel.tsx:82(misleading disabled-reason text): Documentation/UX issues, not security.Copilot flagged
prompt.ts:183(mixed PR/MR terminology for GitLab): Cosmetic correctness issue, not security.Copilot flagged
sync.ts:30(sameShadocstring vs. implementation divergence): A correctness concern about SHA comparison, but not exploitable as a security vulnerability — the worst case is a false-negative "SHAs match" on a very short string, not an attacker-controlled outcome.Copilot flagged
BitbucketWebhooksSection.tsx:261("Skip certificate verification" lacks a security warning in the UI): The setting tells Bitbucket's servers to skip TLS verification on outbound webhook delivery — it doesn't affect the app's own TLS. While a missing warning is a UX concern, there is no attack path an external adversary can exercise without the user first deliberately enabling the toggle; it does not clear the Low-severity confidence threshold (9/10).Areas I investigated that turned out clean:
Credential handling over IPC:
BbAccountInfonever includes the token; forge secrets arepub(crate)only and never returned by any#[tauri::command].Pagination
nextURL followingworkspace_members/pr_tasks: ThenextURL comes from Bitbucket's own HTTPS API response and is sent directly toresolve_url, which passes absolute URLs through tobb_get_jsonwith Basic auth. There is no host-pinning check. However, thenextURL is server-generated (not user-controlled), its origin is protected by TLS (api.bitbucket.org), and compromising it requires breaking Bitbucket's TLS or server — which is outside the threat model here. Similarly for theLocationheader followed inpoll_merge_task.BBQL injection in
prs_for_branch: The"and\block combined withencode_query_valuepercent-encoding of the complete query parameter is adequate for the BBQL context.encode_uuidpath segments: All percent-encodes outside the unreserved set; path traversal sequences like../are safely encoded before use in API paths.External review content in AI prompts: This is the product working as designed (PR comment content → AI review context). The model has no tool access or security decision capability in the review flow, so this is not a cross-privilege-boundary XPIA scenario.
Originally posted by: theBGuy
AI review (sonnet) · automated
Resolved since last review
publish_repocredential sentinel — The module-level doc and function comment now explicitly state (with a "probe-validated" note) thatx-bitbucket-api-token-authis the correct sentinel for git-over-HTTPS, distinct from the email:token Basic pair used by the REST API. The previous finding was a false positive; the code was already correct.rewritten_origin_urlproxy URL clobbering — Fixed: the HTTPS branch now extracts the authority, strips userinfo/port, and bails withNoneunless the host isbitbucket.org(case-insensitive). The SCP branch similarly guards on the host after@, sogit@github.com:…URLs are left alone. New unit tests cover both cases.pr_tasksmissingpagelen— Fixed: first-page URL is now…/tasks?pagelen=100.resolve_pipelinebroad error catch — Fixed: usesbb_get_text_statusto inspect the raw HTTP status, only falling through to the query-list fallback on a genuine 404; auth errors (401, 429, …) surface immediately without a doomed second request.Copilot's findings are all confirmed resolved: the
ForgeNotReadydoc comment is updated, the "isn't supported yet" copy inPullRequestsPanelwas replaced with the connect-account prompt,prompt.tsnow replaces "issue or PR numbers" correctly,sync.ts'ssameShadocstring correctly documents the ≥7-char guard as prefix-path-only, and the TLS warning appears whenskipCertVerificationis toggled on.The corrected PR is in good shape. All four previous findings are cleanly addressed and the Copilot findings are genuinely resolved in the current diff. No new issues are visible in the changed code.
Originally posted by: theBGuy
AI review (sonnet) · automated
Now let me look at some of the frontend pieces, particularly the new external reviews gating and the
forgeJobLogschange.Let me look at the external-context changes and some more truncated files to check key concerns.This is a large, well-structured Bitbucket Cloud integration touching 60+ files (5000+ lines in the Rust provider, extensive frontend additions, docs). The implementation follows the established GitLab pattern faithfully and includes solid unit-test coverage for all pure logic. No blockers — the change is ready.Resolved since last review
The following prior findings are confirmed fixed in the current diff:
display_name: Noneinaccount()—KEY_DISPLAY_NAMEis now persisted atset_accounttime, read inaccount(), and deleted inclear_account(). ✓Ok—list_reposnow tracksworkspace_count/any_ok/last_errand surfaces the error when every workspace fetch fails. ✓PrTasksSectionUI state leaking across PR switches —useEffectkeyed onnumberresets all edit/add state. ✓onChangefires inReviewersPopover/AssigneesPopover— both now compare draft vs. value set before callingonChange. ✓DebugJobDialogcallingforgeCiJobLogsfor Bitbucket jobs — now routes throughforgeJobLogs, which dispatches onjob.logRef. ✓useExternalReviewsnow gates onforge.isSuccess && provider != null. ✓buildReviewPromptin automations —runner.tsresolves the provider once and threads it to bothresolveExternalContextand thebuildReviewPromptcall. ✓is_bot: truebypass allowing human inline comments to pose as AI findings —isReviewerFindingnow branches onprovider === "gitlab"and requires theREVIEWER_BOTSallowlist for every kind (not justcomment), and the interactive review path (reviews.ts:357) now threadscontext.provider. ✓tasksKey/bbVariablesKeylocal duplicates — both promoted to exports fromqueries.ts; component-local copies removed. ✓Performance
should-fix —
run_failed_logsre-resolves credentials and workspace/slug for every failed step.bitbucket.rs,run_failed_logs(~line 1449):step_logsstarts withhttp::load_credentials().await?andworkspace_slug(repo_path).await?— the latter spawns agit remote get-url originsubprocess. The outerrun_failed_logsalready hascreds,ws, andslugin scope. For a pipeline with N failed steps this is N redundant keyring reads and N redundant git subprocess invocations on top of the initial ones (on Windows, subprocess spawn cost is non-trivial).Suggested fix: extract a
step_log_raw(creds: &BbCredentials, ws: &str, slug: &str, pipeline_uuid: &str, step_uuid: &str)private helper that contains the inner HTTP logic; have the publicstep_logscommand resolve credentials/workspace and delegate to it; call that helper directly from the loop inrun_failed_logs.Correctness
nit —
viewer_did_author: falsecreates a one-way comment surface for Bitbucket.bitbucket.rs,from_bb_comment(~line 991):Since
comment_pris implemented, users can post comments but the UI will never show Edit/Delete on their own comments (those controls gate onviewer_did_author). The comment author UUID is available viac.user.as_ref().and_then(|u| u.uuid.as_deref())and could be compared against the viewer UUID resolved by aGET /2.0/usercall (the same callpr_approvalsalready does). This is likely a deliberate v1 limitation (Bitbucket comment-delete may not yet be wired at theforge_pr_delete_commentdispatch level, so showing the button would just produce an error), but it's worth documenting explicitly in a// TODOor the help guide if it's intentional.Minor
nit —
encode_query_valueis named for query values but used for path segments too.forge/mod.rsandforge/bitbucket.rs(e.g.repo_base):These are path segments, not query values; the function happens to over-encode safely (RFC-3986 unreserved encoding is stricter than path-segment encoding), but a name like
encode_uri_componentwould better signal dual use. Low priority, but the mismatch could confuse future readers.Ticket changed by: theBGuy