-
Notifications
You must be signed in to change notification settings - Fork 57
Apply Git branch dropdown select #1416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: vinokurig The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
| export async function getBranches(url: string): Promise<api.IGitBranches | undefined> { | ||
| try { | ||
| return new Promise((resolve, reject) => { | ||
| run(`git`, ['ls-remote', '--refs', url], 1000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vinokurig please sanitize the url to prevent injections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to do anything with URLs here; moreover, modifying the URL here can add some edge cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the best approach to protect backend just to validate the target URL with provider patterns:
/packages/dashboard-frontend/src/store/Workspaces/Preferences/helpers.ts
export const gitProviderPatterns = {
github: {
https: /^https:\/\/github\.com\/([^\\/]+\/[^\\/]+)(?:\/.*)?$/,
ssh: /^(?:(?:git\+)?ssh:\/\/)?git@github\.com:([^\\/]+\/[^\\/]+?)(?:\.git)?$/,
},
gitlab: {
https: /^https:\/\/gitlab\.com\/([^\\/]+\/[^\\/]+)(?:\/.*)?$/,
ssh: /^(?:(?:git\+)?ssh:\/\/)?git@gitlab\.com:([^\\/]+\/[^\\/]+?)(?:\.git)?$/,
},
bitbucket: {
https: /^https:\/\/bitbucket\.org\/([^\\/]+\/[^\\/]+)(?:\/.*)?$/,
ssh: /^(?:(?:git\+)?ssh:\/\/)?git@bitbucket\.org:([^\\/]+\/[^\\/]+?)(?:\.git)?$/,
},
azureDevOps: {
https: /^https:\/\/(?:\w+@)?dev\.azure\.com\/([^\\/]+\/[^\\/]+\/_git\/[^\\/]+?)(?:\?.*)?$/,
ssh: /^(?:ssh:\/\/)?git@ssh\.dev\.azure\.com:v3\/([^\\/]+\/[^\\/]+\/[^\\/]+)(?:\/[^\\/]+)?$/,
},
};
If it is valid - execute code. If not - return the proper error.
@akurinnoy WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could also add a time-limited cashing option here. Since multiple users may be requesting the same repository(It is optional)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this we will limit the functionality to just 4 supported git providers, server version of supported git providers will not be included. On the other hand, this will protect the dashboard container from executing git ls-remote with suspicious urls.
My proposal is to add a simple http https ssh validation and probably a character quantity limit.
@akurinnoy @svor We need to make a decision here: limited functionality or security protection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have to support GitLab and Bitbucket servers as well. This providers may have custom host URLs, I'm not sure if the pattern provided above will work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@olexii4 @vinokurig @svor I would also prefer more general URL validation to exclude possible injections.
@olexii4 Regarding the server-side caching, I would rather disagree, because this will introduce security issues. But, the client-side cashing sounds reasonable to me.
packages/dashboard-frontend/src/components/ImportFromGit/RepoOptionsAccordion/index.tsx
Show resolved
Hide resolved
04f74f5 to
f8826a9
Compare
|
Docker image build succeeded: quay.io/eclipse/che-dashboard:pr-1416 kubectl patch commandkubectl patch -n eclipse-che "checluster/eclipse-che" --type=json -p="[{"op": "replace", "path": "/spec/components/dashboard/deployment", "value": {containers: [{image: "quay.io/eclipse/che-dashboard:pr-1416", name: che-dashboard}]}}]" |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1416 +/- ##
==========================================
- Coverage 92.18% 92.14% -0.04%
==========================================
Files 511 514 +3
Lines 47614 47926 +312
Branches 3432 3472 +40
==========================================
+ Hits 43891 44161 +270
- Misses 3682 3726 +44
+ Partials 41 39 -2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| export async function getBranches(url: string): Promise<api.IGitBranches | undefined> { | ||
| try { | ||
| return new Promise((resolve, reject) => { | ||
| run(`git`, ['ls-remote', '--refs', url], 1000) |
Check failure
Code scanning / CodeQL
Second order command injection High
a user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 3 days ago
The safest and most effective fix is to validate the url argument in the getBranches function before invoking the run command. This ensures that it cannot begin with a dash (i.e., -) or contain a string that would cause it to be interpreted as a git option rather than a repository address. The validation should accept only valid Git remote URLs (e.g., those beginning with git@, http://, https://, ssh://, or that conform to accepted address formats), and reject any value that could be used for command injection.
The change should be made directly inside the getBranches function, as that is the main sink for the tainted value.
The fix requires either a simple check (rejecting any value that starts with a dash), or a more rigorous check (accepting only URLs that match expected patterns).
-
Copy modified lines R18-R26
| @@ -15,6 +15,15 @@ | ||
| import { run } from '@/devworkspaceClient/services/helpers/exec'; | ||
|
|
||
| export async function getBranches(url: string): Promise<api.IGitBranches | undefined> { | ||
| // Disallow remote URLs that start with "-" (prevent git option injection) | ||
| // Optionally, only allow typical git URLs: git@, http(s)://, ssh:// | ||
| if ( | ||
| typeof url !== 'string' || | ||
| url.startsWith('-') || | ||
| !(url.startsWith('git@') || url.startsWith('https://') || url.startsWith('http://') || url.startsWith('ssh://')) | ||
| ) { | ||
| throw new Error('Invalid remote URL: ' + url); | ||
| } | ||
| try { | ||
| return new Promise((resolve, reject) => { | ||
| run(`git`, ['ls-remote', '--refs', url], 1000) |
|
Docker image build succeeded: quay.io/eclipse/che-dashboard:pr-1416 kubectl patch commandkubectl patch -n eclipse-che "checluster/eclipse-che" --type=json -p="[{"op": "replace", "path": "/spec/components/dashboard/deployment", "value": {containers: [{image: "quay.io/eclipse/che-dashboard:pr-1416", name: che-dashboard}]}}]" |
| FROM docker.io/node:18.19.1-alpine3.19 | ||
|
|
||
| RUN apk --no-cache add curl | ||
| RUN apk --no-cache add curl git |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t forget to include Git in the downstream image
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| export function run(cmd: string, args?: string[], timeOut?: number): Promise<string> { | ||
| let command: ChildProcessWithoutNullStreams; | ||
| const promise: Promise<string> = new Promise((resolve, reject) => { | ||
| command = spawn(cmd, args); | ||
| let result = ''; | ||
| command.stdout.on('data', data => { | ||
| result += data.toString(); | ||
| }); | ||
| command.on('close', () => { | ||
| resolve(result.replace(/\n/g, ' ').trim()); | ||
| }); | ||
| command.on('error', err => { | ||
| reject(err); | ||
| }); | ||
| }); | ||
| if (timeOut && timeOut > 0) { | ||
| const timeoutPromise: Promise<string> = new Promise((_, reject) => { | ||
| setTimeout(() => { | ||
| if (command && command.exitCode == null) { | ||
| command.kill(); | ||
| reject(new Error('Timeout exceeded')); | ||
| } | ||
| }, timeOut); | ||
| }); | ||
| return Promise.race([promise, timeoutPromise]); | ||
| } else { | ||
| return promise; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, use async/await for better readability and maintainability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to define the promises explicitly for the Promise.race function.
| run(`git`, ['ls-remote', '--refs', url], 1000) | ||
| .then(result => { | ||
| resolve({ | ||
| branches: result | ||
| .split(' ') | ||
| .filter(b => b.indexOf('refs/heads/') > 0 || b.indexOf('refs/tags/') > 0) | ||
| .map(b => b.replace(new RegExp('.*\\trefs/((heads)|(tags))/'), '')), | ||
| }); | ||
| }) | ||
| .catch(err => reject(err)); | ||
| }); | ||
| } catch (error) { | ||
| return undefined; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, use async/await for better readability and maintainability.
| fetchGitBranches(getRepositoryUrlFromLocation(location)) | ||
| .then(branches => { | ||
| state.branchList = branches.branches; | ||
| this.setState(state); | ||
| }) | ||
| .catch(() => { | ||
| state.branchList = undefined; | ||
| this.setState(state); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, use async/await for better readability and maintainability.
What does this PR do?
git ls-remote <repo url>Git Branchsection:Screenshot/screencast of this PR
What issues does this PR fix or reference?
fixes eclipse-che/che#23171
Is it tested? How?
quay.io/eclipse/che-dashboard:pr-Import from Gitsection.Git Repo Optionsand click theGit Branchdropdown, see: Git branches list is revealed.https://github.com/vinokurig/t, see: empty branch list warning is shown:Release Notes
Docs PR