security: address remaining code scanning alerts (#253)
This commit is contained in:
@@ -282,17 +282,16 @@ function InstallGitHubApp(props: {
|
||||
return;
|
||||
}
|
||||
const repoWarnings: Warning[] = [];
|
||||
if (repoName_1.includes('github.com')) {
|
||||
const slug = extractGitHubRepoSlug(repoName_1);
|
||||
if (!slug) {
|
||||
repoWarnings.push({
|
||||
title: 'Invalid GitHub URL format',
|
||||
message: 'The repository URL format appears to be invalid.',
|
||||
instructions: ['Use format: owner/repo or https://github.com/owner/repo', 'Example: anthropics/claude-cli']
|
||||
});
|
||||
} else {
|
||||
repoName_1 = slug;
|
||||
}
|
||||
const slug = extractGitHubRepoSlug(repoName_1);
|
||||
const isUrlLike = /^[a-z][a-z0-9+.-]*:\/\//i.test(repoName_1) || repoName_1.startsWith('www.');
|
||||
if (slug) {
|
||||
repoName_1 = slug;
|
||||
} else if (isUrlLike) {
|
||||
repoWarnings.push({
|
||||
title: 'Invalid GitHub URL format',
|
||||
message: 'The repository URL format appears to be invalid.',
|
||||
instructions: ['Use format: owner/repo or https://github.com/owner/repo', 'Example: anthropics/claude-cli']
|
||||
});
|
||||
}
|
||||
if (!repoName_1.includes('/')) {
|
||||
repoWarnings.push({
|
||||
|
||||
@@ -33,4 +33,16 @@ test('rejects malformed or non-GitHub URLs', () => {
|
||||
assert.equal(extractGitHubRepoSlug('https://gitlab.com/Gitlawb/openclaude'), null)
|
||||
assert.equal(extractGitHubRepoSlug('https://github.com/Gitlawb'), null)
|
||||
assert.equal(extractGitHubRepoSlug('not actually github.com/Gitlawb/openclaude'), null)
|
||||
assert.equal(
|
||||
extractGitHubRepoSlug('https://evil.example/?next=github.com/Gitlawb/openclaude'),
|
||||
null,
|
||||
)
|
||||
assert.equal(
|
||||
extractGitHubRepoSlug('https://github.com.evil.example/Gitlawb/openclaude'),
|
||||
null,
|
||||
)
|
||||
assert.equal(
|
||||
extractGitHubRepoSlug('https://example.com/github.com/Gitlawb/openclaude'),
|
||||
null,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
export function extractGitHubRepoSlug(value: string): string | null {
|
||||
const trimmed = value.trim()
|
||||
|
||||
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) && !trimmed.includes('github.com')) {
|
||||
return null
|
||||
const slugMatch = trimmed.match(
|
||||
/^(?<owner>[^/:\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?\/?$/i,
|
||||
)
|
||||
if (slugMatch?.groups?.owner && slugMatch.groups.repo) {
|
||||
return `${slugMatch.groups.owner}/${slugMatch.groups.repo}`.replace(
|
||||
/\.git$/i,
|
||||
'',
|
||||
)
|
||||
}
|
||||
|
||||
if (!trimmed.includes('github.com')) {
|
||||
return trimmed
|
||||
const shorthandUrlMatch = trimmed.match(
|
||||
/^(?:https?:\/\/)?(?:www\.)?github\.com\/(?<owner>[^/:\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?\/?$/i,
|
||||
)
|
||||
if (shorthandUrlMatch?.groups?.owner && shorthandUrlMatch.groups.repo) {
|
||||
return `${shorthandUrlMatch.groups.owner}/${shorthandUrlMatch.groups.repo}`.replace(
|
||||
/\.git$/i,
|
||||
'',
|
||||
)
|
||||
}
|
||||
|
||||
const sshMatch = trimmed.match(
|
||||
@@ -16,6 +28,10 @@ export function extractGitHubRepoSlug(value: string): string | null {
|
||||
return `${sshMatch.groups.owner}/${sshMatch.groups.repo}`
|
||||
}
|
||||
|
||||
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = new URL(trimmed)
|
||||
const hostname = parsed.hostname.toLowerCase()
|
||||
|
||||
Reference in New Issue
Block a user