Skip to content

feat(x2a): bitbucket git repos support#2510

Open
mareklibra wants to merge 3 commits intoredhat-developer:mainfrom
mareklibra:FLPATH-3389.Bitbucket2
Open

feat(x2a): bitbucket git repos support#2510
mareklibra wants to merge 3 commits intoredhat-developer:mainfrom
mareklibra:FLPATH-3389.Bitbucket2

Conversation

@mareklibra
Copy link
Member

@mareklibra mareklibra commented Mar 11, 2026

Fixes: FLPATH-3389

The application authentication plus source and target repos newly support the Bitbucket.

App authentication configuration works by following https://backstage.io/docs/auth/bitbucket/provider/.

OAuth token scopes and other requirements are documented in the README, there are specifics especially regarding username and resolver.

Tested on a private Bitbucket project (so requiring authenticated access), both git push and git pull.

Works well against cloud-based Bitbucket.
In case there is a requirement for a Bitbucket server (self-hosted, custom domain), additional work is needed, especially around SCN provider detection (so far naive implementation via parsing URL).

The source and target repos can be freely combined or reused.


Test matrix:

Screenshot From 2026-03-12 09-08-09

@rhdh-gh-app
Copy link

rhdh-gh-app bot commented Mar 11, 2026

Changed Packages

Package Name Package Path Changeset Bump Current Version
app workspaces/x2a/packages/app none v0.0.0
backend workspaces/x2a/packages/backend none v0.0.0
@red-hat-developer-hub/backstage-plugin-scaffolder-backend-module-x2a workspaces/x2a/plugins/scaffolder-backend-module-x2a patch v0.1.2
@red-hat-developer-hub/backstage-plugin-x2a-common workspaces/x2a/plugins/x2a-common patch v1.0.2
@red-hat-developer-hub/backstage-plugin-x2a workspaces/x2a/plugins/x2a patch v1.0.2

@rhdh-qodo-merge
Copy link

Review Summary by Qodo

Add Bitbucket cloud repository support with OAuth authentication

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add Bitbucket cloud repository support for authentication and operations
• Implement Bitbucket-specific token formatting and OAuth scope handling
• Support Bitbucket URL normalization in RepoUrlPicker component
• Add Bitbucket branch and artifact URL builders with proper formatting
Diagram
flowchart LR
  A["Bitbucket OAuth"] -->|"Auth Module"| B["Backend Registration"]
  C["Repository URL"] -->|"getScmProvider"| D["Detect Bitbucket"]
  D -->|"getAuthTokenDescriptor"| E["OAuth Scopes"]
  E -->|"augmentRepoToken"| F["x-token-auth: token"]
  C -->|"normalizeRepoUrl"| G["Clone URL"]
  G -->|"buildRepoBranchUrl"| H["Branch URL"]
  G -->|"buildArtifactUrl"| I["Artifact URL"]
  F -->|"useRepoAuth"| J["Authenticated Operations"]
Loading

Grey Divider

File Changes

1. workspaces/x2a/packages/backend/src/index.ts ⚙️ Configuration changes +3/-0

Register Bitbucket auth and scaffolder backend modules

workspaces/x2a/packages/backend/src/index.ts


2. workspaces/x2a/packages/backend/package.json Dependencies +2/-0

Add Bitbucket provider dependencies to backend

workspaces/x2a/packages/backend/package.json


3. workspaces/x2a/packages/app/src/App.tsx ✨ Enhancement +11/-1

Add Bitbucket auth provider to sign-in options

workspaces/x2a/packages/app/src/App.tsx


View more (19)
4. workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts ✨ Enhancement +15/-3

Detect Bitbucket provider from repository URLs

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts


5. workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts 🧪 Tests +13/-2

Test Bitbucket provider detection logic

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts


6. workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.ts ✨ Enhancement +9/-1

Return Bitbucket-specific OAuth scopes per access level

workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.ts


7. workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.test.ts 🧪 Tests +91/-0

Test Bitbucket OAuth scope selection

workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.test.ts


8. workspaces/x2a/plugins/x2a-common/src/utils/augmentRepoToken.ts ✨ Enhancement +7/-1

Format Bitbucket tokens with x-token-auth prefix

workspaces/x2a/plugins/x2a-common/src/utils/augmentRepoToken.ts


9. workspaces/x2a/plugins/x2a-common/src/utils/augmentRepoToken.test.ts 🧪 Tests +54/-0

Test Bitbucket token augmentation logic

workspaces/x2a/plugins/x2a-common/src/utils/augmentRepoToken.test.ts


10. workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts ✨ Enhancement +14/-5

Support Bitbucket workspace-based URL normalization

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts


11. workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.test.ts 🧪 Tests +28/-0

Test Bitbucket URL normalization from RepoUrlPicker

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.test.ts


12. workspaces/x2a/plugins/x2a-common/report.api.md 📝 Documentation +1/-1

Update API report with Bitbucket provider type

workspaces/x2a/plugins/x2a-common/report.api.md


13. workspaces/x2a/plugins/x2a/src/repoAuth/useRepoAuth.ts ✨ Enhancement +55/-9

Add Bitbucket auth API integration with optional provider support

workspaces/x2a/plugins/x2a/src/repoAuth/useRepoAuth.ts


14. workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts ✨ Enhancement +7/-4

Build Bitbucket branch URLs with correct format

workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts


15. workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.test.ts 🧪 Tests +20/-4

Test Bitbucket branch URL construction

workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.test.ts


16. workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.ts ✨ Enhancement +4/-1

Build Bitbucket artifact URLs with src path format

workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.ts


17. workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.test.ts 🧪 Tests +32/-0

Test Bitbucket artifact URL construction

workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.test.ts


18. workspaces/x2a/README.md 📝 Documentation +10/-0

Document Bitbucket OAuth setup and configuration

workspaces/x2a/README.md


19. workspaces/x2a/app-config.yaml ⚙️ Configuration changes +11/-0

Add Bitbucket auth provider configuration example

workspaces/x2a/app-config.yaml


20. workspaces/x2a/examples/org.yaml 📝 Documentation +4/-0

Add Bitbucket username annotation example for users

workspaces/x2a/examples/org.yaml


21. workspaces/x2a/templates/conversion-project-template.yaml ⚙️ Configuration changes +2/-0

Add bitbucket.org to allowed RepoUrlPicker hosts

workspaces/x2a/templates/conversion-project-template.yaml


22. workspaces/x2a/.changeset/two-taxis-lose.md 📝 Documentation +6/-0

Document Bitbucket support addition in changelog

workspaces/x2a/.changeset/two-taxis-lose.md


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Mar 11, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Repo URL leaked to logs 🐞 Bug ⛨ Security
Description
normalizeRepoUrl now logs the raw repo URL on parse errors, which can expose credentials/tokens if
users paste authenticated clone URLs, and it also generates noisy error logs for already-normalized
URLs with a scheme (since the function prefixes input with "https://"). This runs on backend user
input during project creation.
Code

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts[R63-65]

+    // eslint-disable-next-line no-console
+    console.error(`Error normalizing repo URL: ${url}`, error);
+    // falls through to return the original URL
Evidence
normalizeRepoUrl always attempts to parse via new URL(https://${url.trim()}), and on any failure
it logs url verbatim via console.error(...). The function is explicitly expected to accept
already-normalized full clone URLs (test asserts it returns them unchanged), which will fail parsing
because they already contain https:// and therefore will take the catch/log path. This function is
invoked in the backend scaffolder action on user-provided repo URLs during project creation.

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts[36-66]
workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.test.ts[52-55]
workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts[149-156]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`normalizeRepoUrl` logs the raw input URL on parse failures. This can leak credentials/tokens embedded in clone URLs, and it will also emit noisy errors for already-normalized URLs that include a scheme because the function prefixes input with `https://`.

### Issue Context
- `normalizeRepoUrl` is used on backend user input during scaffolder project creation.
- Tests indicate full clone URLs should be accepted and returned unchanged.

### Fix Focus Areas
- workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts[36-69]
- workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts[149-156]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Bitbucket env vars non-optional 🐞 Bug ⛯ Reliability
Description
The workspace app-config adds a Bitbucket auth provider using required env substitutions without
defaults; if these env vars are not set, Backstage config resolution may fail at startup (or the
provider may be unusable), impacting dev/CI setups that don’t need Bitbucket yet.
Code

workspaces/x2a/app-config.yaml[R95-105]

+    bitbucket:
+      # Follow https://backstage.io/docs/auth/bitbucket/provider/
+      development:
+        clientId: ${AUTH_BITBUCKET_CLIENT_ID}
+        clientSecret: ${AUTH_BITBUCKET_CLIENT_SECRET}
+        signIn:
+          resolvers:
+            # The usernameMatchingUserEntityName resolver is not working for Bitbucket.
+            # Cannot make the bitbucket to provide user's email.
+            # See the org.yaml file for an example of how to use the usernameMatchingUserEntityAnnotation resolver.
+            - resolver: usernameMatchingUserEntityAnnotation
Evidence
Bitbucket auth config uses ${AUTH_BITBUCKET_CLIENT_ID} / ${AUTH_BITBUCKET_CLIENT_SECRET} without
defaults. The same app-config file already uses the :-default syntax elsewhere, indicating
optional defaults are supported and could be applied here to prevent hard failures when Bitbucket is
not configured.

workspaces/x2a/app-config.yaml[73-105]
workspaces/x2a/app-config.yaml[145-154]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The workspace `app-config.yaml` introduces Bitbucket auth config with required env substitutions. This may break environments where Bitbucket isn’t configured.

### Issue Context
The same config file already uses `:-default` env-var defaults for other settings, so applying similar defaults for Bitbucket is consistent.

### Fix Focus Areas
- workspaces/x2a/app-config.yaml[95-105]
- workspaces/x2a/app-config.yaml[145-154]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@mareklibra mareklibra force-pushed the FLPATH-3389.Bitbucket2 branch 2 times, most recently from adb1860 to ddf3fd8 Compare March 11, 2026 06:44
@mareklibra mareklibra force-pushed the FLPATH-3389.Bitbucket2 branch from ddf3fd8 to ebecdaf Compare March 11, 2026 06:58
Copy link
Contributor

@eloycoto eloycoto left a comment

Choose a reason for hiding this comment

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

I have the feeling that we should move this to auth/provider/github.ts, auth/provider/gitlab.ts and auth/provider/bitbucket.ts

This is getting super complicated, and we should implement some kind of traits, on how the AuthN is done.

We have enough time to deliver this, can you do a refactor here?

@mareklibra mareklibra marked this pull request as draft March 11, 2026 12:06
@mareklibra
Copy link
Member Author

I can do such refactoring, it will be easier to maintain if we need to add another provider.

I will explore how to detect the provider in a more robust way than just via URL matching.

Signed-off-by: Marek Libra <marek.libra@gmail.com>
@mareklibra mareklibra force-pushed the FLPATH-3389.Bitbucket2 branch 2 times, most recently from da0c9ec to c36b94d Compare March 12, 2026 07:58
@mareklibra mareklibra marked this pull request as ready for review March 12, 2026 08:09
@rhdh-qodo-merge
Copy link

Review Summary by Qodo

Add Bitbucket Cloud support with SCM provider abstraction layer

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add Bitbucket Cloud repository support for authentication and scaffolding
• Refactor SCM provider abstraction layer with configurable host detection
• Support custom-domain SCM hosts via integrations: config section
• Update token augmentation and URL building for provider-specific formats
Diagram
flowchart LR
  Config["Backstage Config<br/>integrations:"]
  BuildMap["buildScmHostMap()"]
  ResolveProvider["resolveScmProvider()"]
  Provider["ScmProvider<br/>github/gitlab/bitbucket"]
  Token["augmentToken()"]
  URL["buildBranchUrl()<br/>buildArtifactUrl()"]
  
  Config -->|reads| BuildMap
  BuildMap -->|creates| ResolveProvider
  ResolveProvider -->|returns| Provider
  Provider -->|formats| Token
  Provider -->|generates| URL
Loading

Grey Divider

File Changes

1. workspaces/x2a/plugins/x2a-common/src/scm/ScmProvider.ts ✨ Enhancement +58/-0

Define ScmProvider interface and ScmProviderName type

workspaces/x2a/plugins/x2a-common/src/scm/ScmProvider.ts


2. workspaces/x2a/plugins/x2a-common/src/scm/providers/github.ts ✨ Enhancement +44/-0

Implement GitHub SCM provider with token and URL handling

workspaces/x2a/plugins/x2a-common/src/scm/providers/github.ts


3. workspaces/x2a/plugins/x2a-common/src/scm/providers/gitlab.ts ✨ Enhancement +48/-0

Implement GitLab SCM provider with fallback behavior

workspaces/x2a/plugins/x2a-common/src/scm/providers/gitlab.ts


View more (42)
4. workspaces/x2a/plugins/x2a-common/src/scm/providers/bitbucket.ts ✨ Enhancement +46/-0

Implement Bitbucket Cloud SCM provider with specific token format

workspaces/x2a/plugins/x2a-common/src/scm/providers/bitbucket.ts


5. workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts ✨ Enhancement +81/-0

Create provider registry with URL and config-based detection

workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts


6. workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts ✨ Enhancement +68/-0

Build host-to-provider mapping from Backstage integrations config

workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts


7. workspaces/x2a/plugins/x2a-common/src/scm/index.ts ✨ Enhancement +23/-0

Export SCM provider types and functions

workspaces/x2a/plugins/x2a-common/src/scm/index.ts


8. workspaces/x2a/plugins/x2a-common/src/scm/providers/providers.test.ts 🧪 Tests +124/-0

Test all three SCM provider implementations

workspaces/x2a/plugins/x2a-common/src/scm/providers/providers.test.ts


9. workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.test.ts 🧪 Tests +124/-0

Test provider resolution with config and URL heuristics

workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.test.ts


10. workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.test.ts 🧪 Tests +94/-0

Test host map building from integrations config

workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.test.ts


11. workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts ✨ Enhancement +14/-5

Add Bitbucket workspace-based URL normalization support

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts


12. workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.test.ts 🧪 Tests +28/-0

Test Bitbucket URL normalization with workspace parameter

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.test.ts


13. workspaces/x2a/plugins/x2a-common/src/utils/index.ts Refactoring +0/-3

Remove deprecated SCM provider utility exports

workspaces/x2a/plugins/x2a-common/src/utils/index.ts


14. workspaces/x2a/plugins/x2a-common/src/index.ts ✨ Enhancement +1/-0

Export new SCM provider module

workspaces/x2a/plugins/x2a-common/src/index.ts


15. workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts Refactoring +13/-16

Refactor to use new ScmProvider abstraction with config

workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts


16. workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.test.ts 🧪 Tests +20/-18

Update tests to pass config parameter to action

workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.test.ts


17. workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/module.ts ✨ Enhancement +5/-2

Inject rootConfig into createProjectAction

workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/module.ts


18. workspaces/x2a/plugins/scaffolder-backend-module-x2a/package.json Dependencies +1/-0

Add @backstage/config dependency

workspaces/x2a/plugins/scaffolder-backend-module-x2a/package.json


19. workspaces/x2a/plugins/x2a-common/package.json Dependencies +1/-0

Add @backstage/config dependency for SCM host map building

workspaces/x2a/plugins/x2a-common/package.json


20. workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.ts Refactoring +8/-9

Refactor to use ScmProvider for URL building

workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.ts


21. workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.test.ts 🧪 Tests +32/-0

Add Bitbucket artifact URL building tests

workspaces/x2a/plugins/x2a/src/components/tools/buildArtifactUrl.test.ts


22. workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts Refactoring +13/-11

Refactor to use ScmProvider for branch URL building

workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.ts


23. workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.test.ts 🧪 Tests +20/-4

Add Bitbucket branch URL building tests

workspaces/x2a/plugins/x2a/src/components/tools/buildRepoBranchUrl.test.ts


24. workspaces/x2a/plugins/x2a/src/hooks/useScmHostMap.ts ✨ Enhancement +33/-0

Create hook to read SCM host map from config

workspaces/x2a/plugins/x2a/src/hooks/useScmHostMap.ts


25. workspaces/x2a/plugins/x2a/src/repoAuth/useRepoAuth.ts ✨ Enhancement +54/-20

Add Bitbucket auth API support and provider-agnostic token handling

workspaces/x2a/plugins/x2a/src/repoAuth/useRepoAuth.ts


26. workspaces/x2a/plugins/x2a/src/hooks/useBulkRun.ts Refactoring +12/-11

Use ScmProvider for token augmentation in bulk runs

workspaces/x2a/plugins/x2a/src/hooks/useBulkRun.ts


27. workspaces/x2a/plugins/x2a/src/hooks/useBulkRun.test.ts 🧪 Tests +8/-4

Update mocks for new ScmProvider-based token handling

workspaces/x2a/plugins/x2a/src/hooks/useBulkRun.test.ts


28. workspaces/x2a/plugins/x2a/src/components/ArtifactLink.tsx ✨ Enhancement +8/-1

Use host map for artifact URL building

workspaces/x2a/plugins/x2a/src/components/ArtifactLink.tsx


29. workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx Refactoring +12/-10

Use ScmProvider for repository authentication

workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx


30. workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx Refactoring +12/-9

Use ScmProvider for repository authentication in module operations

workspaces/x2a/plugins/x2a/src/components/ModuleTable/ModuleTable.tsx


31. workspaces/x2a/plugins/x2a/src/components/Repository.tsx ✨ Enhancement +3/-1

Use host map for branch URL building

workspaces/x2a/plugins/x2a/src/components/Repository.tsx


32. workspaces/x2a/packages/app/src/App.tsx ✨ Enhancement +11/-1

Add Bitbucket auth provider to sign-in options

workspaces/x2a/packages/app/src/App.tsx


33. workspaces/x2a/packages/backend/src/index.ts ✨ Enhancement +3/-0

Register Bitbucket scaffolder and auth backend modules

workspaces/x2a/packages/backend/src/index.ts


34. workspaces/x2a/packages/backend/package.json Dependencies +2/-0

Add Bitbucket scaffolder and auth backend module dependencies

workspaces/x2a/packages/backend/package.json


35. workspaces/x2a/app-config.yaml ⚙️ Configuration changes +27/-15

Configure integrations section with SCM hosts for provider detection

workspaces/x2a/app-config.yaml


36. workspaces/x2a/templates/conversion-project-template.yaml ⚙️ Configuration changes +2/-0

Add bitbucket.org to allowed repository hosts

workspaces/x2a/templates/conversion-project-template.yaml


37. workspaces/x2a/README.md 📝 Documentation +42/-0

Document Bitbucket OAuth setup and SCM provider detection

workspaces/x2a/README.md


38. workspaces/x2a/examples/org.yaml 📝 Documentation +4/-0

Add Bitbucket username annotation example for user resolver

workspaces/x2a/examples/org.yaml


39. workspaces/x2a/plugins/x2a-common/report.api.md 📝 Documentation +29/-8

Update API report with new ScmProvider types and functions

workspaces/x2a/plugins/x2a-common/report.api.md


40. workspaces/x2a/.changeset/two-taxis-lose.md 📝 Documentation +7/-0

Add changeset for Bitbucket repository support

workspaces/x2a/.changeset/two-taxis-lose.md


41. workspaces/x2a/plugins/x2a-common/src/scm/providers/index.ts Additional files +19/-0

...

workspaces/x2a/plugins/x2a-common/src/scm/providers/index.ts


42. workspaces/x2a/plugins/x2a-common/src/utils/augmentRepoToken.ts Additional files +0/-34

...

workspaces/x2a/plugins/x2a-common/src/utils/augmentRepoToken.ts


43. workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.ts Additional files +0/-47

...

workspaces/x2a/plugins/x2a-common/src/utils/getAuthTokenDescriptor.ts


44. workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts Additional files +0/-35

...

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.test.ts


45. workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts Additional files +0/-25

...

workspaces/x2a/plugins/x2a-common/src/utils/getScmProvider.ts


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Mar 12, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Substring provider misdetection 🐞 Bug ⛨ Security ⭐ New
Description
SCM provider detection falls back to url.includes('github.com') / url.includes('bitbucket.org'),
which can misclassify repositories whose hostname merely contains those substrings (e.g.
github.com.evil.com) and then select the wrong OAuth descriptor and token formatting. This
incorrect provider selection propagates into repo authentication and backend token augmentation,
breaking repository operations and using credentials with an unintended host.
Code

workspaces/x2a/plugins/x2a-common/src/scm/providers/github.ts[R26-35]

+export const githubProvider: ScmProvider = {
+  name: 'github',
+
+  matches: (url: string) => url.includes('github.com'),
+
+  getAuthTokenDescriptor: (_readOnly: boolean) => ({
+    provider: 'github',
+    tokenType: 'oauth' as const,
+    scope: 'repo',
+  }),
Evidence
resolveScmProvider falls back to each provider’s matches(repoUrl) and both GitHub/Bitbucket
providers implement matches via substring checks, not hostname equality. The selected provider is
then used to choose OAuth token descriptors (frontend) and to augment tokens before sending them for
repo operations (backend), so a misclassification directly affects auth/token usage.

workspaces/x2a/plugins/x2a-common/src/scm/providers/github.ts[26-35]
workspaces/x2a/plugins/x2a-common/src/scm/providers/bitbucket.ts[27-36]
workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[53-68]
workspaces/x2a/plugins/x2a/src/components/ModulePage/ModulePage.tsx[84-100]
workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts[140-145]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
SCM provider detection uses substring checks (`includes`) to decide if a repo URL belongs to GitHub/Bitbucket. This can misclassify hosts that merely *contain* those substrings, leading to wrong OAuth token descriptors and token formatting.

### Issue Context
`resolveScmProvider` falls back to provider `matches(repoUrl)` when no config-based host map entry is found. Today, the `matches` implementations are substring-based.

### Fix Focus Areas
- workspaces/x2a/plugins/x2a-common/src/scm/providers/github.ts[26-35]
- workspaces/x2a/plugins/x2a-common/src/scm/providers/bitbucket.ts[27-36]
- workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[30-37]
- workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[53-68]

### Implementation sketch
- Change provider `matches` to parse hostname (or accept hostname as input).
- Prefer: `const host = extractHost(repoUrl); return host === &#x27;github.com&#x27;` (and similarly for bitbucket).
- Add/extend tests for tricky cases like `https://github.com.evil.com/org/repo` to ensure it does **not** match GitHub.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unnormalized host map keys 🐞 Bug ⛯ Reliability ⭐ New
Description
buildScmHostMap stores integrations.*.host values verbatim, but resolveScmProvider looks up
URL.hostname (lowercased, no port), so mixed-case/whitespace/port-included host configs won’t
match and will silently fall back to the wrong provider. This breaks custom-domain SCM detection
even when integrations is configured.
Code

workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts[R55-63]

+  for (const [configKey, providerName] of integrationKeyToProvider) {
+    const entries = config.getOptionalConfigArray(`integrations.${configKey}`);
+    if (entries) {
+      for (const entry of entries) {
+        const host = entry.getOptionalString('host');
+        if (host && !map.has(host)) {
+          map.set(host, providerName);
+        }
+      }
Evidence
The host map uses raw host strings as keys, but lookup uses new URL(...).hostname which is
normalized by the URL parser. When config host strings aren’t already in the canonical hostname
form, map.get(hostname) fails and detection falls back to heuristics/fallback provider without any
signal.

workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts[52-63]
workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[30-34]
workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[57-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`buildScmHostMap` stores raw `integrations.*.host` strings, but `resolveScmProvider` looks up `URL.hostname`. Any non-canonical host values (case, whitespace, ports, or accidental scheme) will never match, silently disabling config-based SCM detection.

### Issue Context
This impacts custom-domain SCM setups where correct provider detection is required for proper OAuth descriptors and token formatting.

### Fix Focus Areas
- workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts[52-63]
- workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.test.ts[20-94]
- workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[30-37]

### Implementation sketch
- In `buildScmHostMap`, normalize:
 - `const raw = entry.getOptionalString(&#x27;host&#x27;);`
 - `const normalized = raw?.trim().toLowerCase();`
 - If you want to accept `host` values containing ports/schemes, canonicalize via URL parsing:
   - `const hostname = new URL(raw.includes(&#x27;://&#x27;) ? raw : `https://${raw}`).hostname;`
- Store `hostname` as the map key.
- Extend tests to include e.g. `host: &#x27;GitHub.COM&#x27;` and/or `host: &#x27;gitlab.internal.io:8443&#x27;` (depending on what formats you decide to support).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Repo URL leaked to logs 🐞 Bug ⛨ Security
Description
normalizeRepoUrl now logs the raw repo URL on parse errors, which can expose credentials/tokens if
users paste authenticated clone URLs, and it also generates noisy error logs for already-normalized
URLs with a scheme (since the function prefixes input with "https://"). This runs on backend user
input during project creation.
Code

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts[R63-65]

+    // eslint-disable-next-line no-console
+    console.error(`Error normalizing repo URL: ${url}`, error);
+    // falls through to return the original URL
Evidence
normalizeRepoUrl always attempts to parse via new URL(https://${url.trim()}), and on any failure
it logs url verbatim via console.error(...). The function is explicitly expected to accept
already-normalized full clone URLs (test asserts it returns them unchanged), which will fail parsing
because they already contain https:// and therefore will take the catch/log path. This function is
invoked in the backend scaffolder action on user-provided repo URLs during project creation.

workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts[36-66]
workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.test.ts[52-55]
workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts[149-156]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`normalizeRepoUrl` logs the raw input URL on parse failures. This can leak credentials/tokens embedded in clone URLs, and it will also emit noisy errors for already-normalized URLs that include a scheme because the function prefixes input with `https://`.
### Issue Context
- `normalizeRepoUrl` is used on backend user input during scaffolder project creation.
- Tests indicate full clone URLs should be accepted and returned unchanged.
### Fix Focus Areas
- workspaces/x2a/plugins/x2a-common/src/utils/normalizeRepoUrl.ts[36-69]
- workspaces/x2a/plugins/scaffolder-backend-module-x2a/src/actions/createProject.ts[149-156]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Bitbucket key forces cloud 🐞 Bug ✓ Correctness ⭐ New
Description
buildScmHostMap maps integrations.bitbucket hosts to the Bitbucket provider even though the
Bitbucket provider is explicitly cloud-only and assumes Bitbucket Cloud URL/token conventions. If
integrations.bitbucket is used for a self-hosted Bitbucket Server, host-map priority will force
Bitbucket Cloud behavior instead of falling back, breaking URLs and token formatting.
Code

workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts[R23-30]

+const integrationKeyToProvider: ReadonlyArray<
+  [configKey: string, provider: ScmProviderName]
+> = [
+  ['github', 'github'],
+  ['gitlab', 'gitlab'],
+  ['bitbucketCloud', 'bitbucket'],
+  ['bitbucket', 'bitbucket'],
+];
Evidence
The Bitbucket provider states it is cloud-only and expects Bitbucket Cloud patterns, but the host
map includes integrations.bitbucket as a source of Bitbucket mappings. Because
resolveScmProvider consults the host map first, any host placed under integrations.bitbucket
will be treated as Bitbucket Cloud even if it is a Bitbucket Server deployment.

workspaces/x2a/plugins/x2a-common/src/scm/providers/bitbucket.ts[19-24]
workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts[23-30]
workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[57-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`buildScmHostMap` maps `integrations.bitbucket` to the Bitbucket provider, but the Bitbucket provider is cloud-only and assumes Bitbucket Cloud URL/token conventions. Because host-map detection has priority, a Bitbucket Server host configured under `integrations.bitbucket` would be forced into Bitbucket Cloud behavior.

### Issue Context
The code comments explicitly state Bitbucket Server should fall through to the GitLab fallback, but the host-map mapping can override that behavior.

### Fix Focus Areas
- workspaces/x2a/plugins/x2a-common/src/scm/buildScmHostMap.ts[23-30]
- workspaces/x2a/plugins/x2a-common/src/scm/providers/bitbucket.ts[19-45]
- workspaces/x2a/plugins/x2a-common/src/scm/providerRegistry.ts[53-68]

### Implementation sketch
- Remove `[&#x27;bitbucket&#x27;, &#x27;bitbucket&#x27;]` from `integrationKeyToProvider`, OR
- Only accept it when the host is exactly `bitbucket.org`, OR
- Add a dedicated `bitbucketServer` provider + corresponding `ScmProviderName` and map `integrations.bitbucket` (or `integrations.bitbucketServer`) to it.
- Add a unit test ensuring a self-hosted Bitbucket Server hostname in the host map does not resolve to the cloud provider unless explicitly intended.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Bitbucket env vars non-optional 🐞 Bug ⛯ Reliability
Description
The workspace app-config adds a Bitbucket auth provider using required env substitutions without
defaults; if these env vars are not set, Backstage config resolution may fail at startup (or the
provider may be unusable), impacting dev/CI setups that don’t need Bitbucket yet.
Code

workspaces/x2a/app-config.yaml[R95-105]

+    bitbucket:
+      # Follow https://backstage.io/docs/auth/bitbucket/provider/
+      development:
+        clientId: ${AUTH_BITBUCKET_CLIENT_ID}
+        clientSecret: ${AUTH_BITBUCKET_CLIENT_SECRET}
+        signIn:
+          resolvers:
+            # The usernameMatchingUserEntityName resolver is not working for Bitbucket.
+            # Cannot make the bitbucket to provide user's email.
+            # See the org.yaml file for an example of how to use the usernameMatchingUserEntityAnnotation resolver.
+            - resolver: usernameMatchingUserEntityAnnotation
Evidence
Bitbucket auth config uses ${AUTH_BITBUCKET_CLIENT_ID} / ${AUTH_BITBUCKET_CLIENT_SECRET} without
defaults. The same app-config file already uses the :-default syntax elsewhere, indicating
optional defaults are supported and could be applied here to prevent hard failures when Bitbucket is
not configured.

workspaces/x2a/app-config.yaml[73-105]
workspaces/x2a/app-config.yaml[145-154]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workspace `app-config.yaml` introduces Bitbucket auth config with required env substitutions. This may break environments where Bitbucket isn’t configured.
### Issue Context
The same config file already uses `:-default` env-var defaults for other settings, so applying similar defaults for Bitbucket is consistent.
### Fix Focus Areas
- workspaces/x2a/app-config.yaml[95-105]
- workspaces/x2a/app-config.yaml[145-154]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

An abstraction layer separating individual providers is introduced.

The providers are detected based on configurable host name, keeping the
URL matching for well-known SCM hostnames as a fallback.

Signed-off-by: Marek Libra <marek.libra@gmail.com>
@mareklibra mareklibra force-pushed the FLPATH-3389.Bitbucket2 branch from c36b94d to fd9c93b Compare March 12, 2026 09:16
Copy link
Contributor

@elai-shalev elai-shalev left a comment

Choose a reason for hiding this comment

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

overall looks great

Comment on lines +46 to +47
buildArtifactUrl: (origin, path, encodedBranch, filePath) =>
`${origin}${path}/-/blob/${encodedBranch}/${filePath}`,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering - is the /blob/ path formation the same across providers? or is it github specific

Copy link
Member Author

Choose a reason for hiding this comment

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

bitbucket has /src/, but github and gitlab use /blob/.

Worked for me... :-)

if (repo.startsWith('https://') || repo.startsWith('http://')) {
const repoUrl = new URL(repo);
const segments = repoUrl.pathname.split('/').filter(Boolean);
repo = segments[segments.length - 1] ?? repo;
Copy link
Contributor

Choose a reason for hiding this comment

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

should we also strip the .git part?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is what goes from the RepoUrlPicker component which is without the ending .git.

This function is called just once from the createProject scaffolder action. Since then, we use the value returned here, so with .git already.

try {
const trimmed = repoUrl.trim();
const urlStr = trimmed.includes('://') ? trimmed : `https://${trimmed}`;
return new URL(urlStr).hostname;
Copy link
Contributor

Choose a reason for hiding this comment

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

edge case: two internal providers (gitlab on port 8080 and gitlab on port 9090) will collide on the map, as the URL() strips the port
we can do new URL(urlStr).host instead

Copy link
Member Author

Choose a reason for hiding this comment

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

good point, fixing it

@mareklibra mareklibra force-pushed the FLPATH-3389.Bitbucket2 branch from 49d1a2b to 503f882 Compare March 12, 2026 10:48
@sonarqubecloud
Copy link

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.

3 participants