-
Notifications
You must be signed in to change notification settings - Fork 735
HELM-520: Warn users about insecure HTTP Helm repository URLs and aut… #16885
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import { tryHttpsUpgrade } from '../helmchartrepository-create-utils'; | ||
|
|
||
| describe('tryHttpsUpgrade', () => { | ||
| const originalFetch = global.fetch; | ||
|
|
||
| afterEach(() => { | ||
| global.fetch = originalFetch; | ||
| jest.useRealTimers(); | ||
| }); | ||
|
|
||
| it('should return https URL when server responds with 200', async () => { | ||
| global.fetch = jest.fn().mockResolvedValue(new Response(null, { status: 200 })); | ||
| const result = await tryHttpsUpgrade('http://example.com/repo'); | ||
| expect(result).toBe('https://example.com/repo'); | ||
| expect(global.fetch).toHaveBeenCalledWith( | ||
| 'https://example.com/repo', | ||
| expect.objectContaining({ method: 'HEAD' }), | ||
| ); | ||
| }); | ||
|
|
||
| it('should return null when server responds with a non-OK status', async () => { | ||
| global.fetch = jest.fn().mockResolvedValue(new Response(null, { status: 404 })); | ||
| const result = await tryHttpsUpgrade('http://example.com/repo'); | ||
| expect(result).toBeNull(); | ||
| }); | ||
|
|
||
| it('should return null when server responds with 500', async () => { | ||
| global.fetch = jest.fn().mockResolvedValue(new Response(null, { status: 500 })); | ||
| const result = await tryHttpsUpgrade('http://example.com/repo'); | ||
| expect(result).toBeNull(); | ||
| }); | ||
|
|
||
| it('should return null when server does not support HTTPS', async () => { | ||
| global.fetch = jest.fn().mockRejectedValue(new TypeError('Failed to fetch')); | ||
| const result = await tryHttpsUpgrade('http://example.com/repo'); | ||
| expect(result).toBeNull(); | ||
| }); | ||
|
|
||
| it('should return null for HTTPS URLs', async () => { | ||
| const result = await tryHttpsUpgrade('https://example.com'); | ||
| expect(result).toBeNull(); | ||
| }); | ||
|
|
||
| it('should return null for OCI URLs', async () => { | ||
| const result = await tryHttpsUpgrade('oci://registry.io/chart'); | ||
| expect(result).toBeNull(); | ||
| }); | ||
|
|
||
| it('should return null for null or undefined input', async () => { | ||
| expect(await tryHttpsUpgrade(null)).toBeNull(); | ||
| expect(await tryHttpsUpgrade(undefined)).toBeNull(); | ||
| }); | ||
|
|
||
| it('should return null when fetch times out', async () => { | ||
| global.fetch = jest.fn().mockImplementation( | ||
| (_url, options) => | ||
| new Promise((_resolve, reject) => { | ||
| options.signal.addEventListener('abort', () => reject(new DOMException('Aborted'))); | ||
| }), | ||
| ); | ||
| jest.useFakeTimers(); | ||
| const promise = tryHttpsUpgrade('http://slow-server.com/repo'); | ||
| jest.advanceTimersByTime(3000); | ||
| const result = await promise; | ||
| expect(result).toBeNull(); | ||
| }); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -72,6 +72,25 @@ export const convertToHelmChartRepository = ( | |
| return newResource; | ||
| }; | ||
|
|
||
| const HTTPS_PROBE_TIMEOUT_MS = 3000; | ||
|
|
||
| export const tryHttpsUpgrade = async (httpUrl: string): Promise<string | null> => { | ||
| if (!httpUrl?.startsWith('http://')) { | ||
| return null; | ||
| } | ||
| const httpsUrl = httpUrl.replace(/^http:\/\//, 'https://'); | ||
|
Comment on lines
+77
to
+81
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
for f in \
frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/helmchartrepository-create-utils.ts \
frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/CreateHelmChartRepositoryFormEditor.tsx \
frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/__tests__/helmchartrepository-create-utils-https-upgrade.spec.ts
do
echo "### $f"
wc -l "$f"
echo
done
echo "### utils excerpt"
sed -n '1,180p' frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/helmchartrepository-create-utils.ts
echo "### form excerpt"
sed -n '120,190p' frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/CreateHelmChartRepositoryFormEditor.tsx
echo "### test excerpt"
sed -n '1,220p' frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/__tests__/helmchartrepository-create-utils-https-upgrade.spec.tsRepository: openshift/console Length of output: 9159 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
files = [
Path('frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/helmchartrepository-create-utils.ts'),
Path('frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/CreateHelmChartRepositoryFormEditor.tsx'),
Path('frontend/packages/helm-plugin/src/components/forms/HelmChartRepository/__tests__/helmchartrepository-create-utils-https-upgrade.spec.ts'),
]
for p in files:
text = p.read_text()
print(f"\n### {p}")
for i, line in enumerate(text.splitlines(), 1):
if 'tryHttpsUpgrade' in line or "startsWith('http://')" in line or 'Alert' in line or 'HTTP is unencrypted' in line or 'https-upgrade' in line:
start = max(1, i-8)
end = min(len(text.splitlines()), i+20)
for j in range(start, end+1):
print(f"{j:4}: {text.splitlines()[j-1]}")
break
PYRepository: openshift/console Length of output: 3792 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n "startsWith\\('http://'\)|startsWith\\(\"http://\"\\)|tryHttpsUpgrade|HTTP is unencrypted" \
frontend/packages/helm-plugin/src/components/forms/HelmChartRepositoryRepository: openshift/console Length of output: 3032 Normalize HTTP scheme checks
📍 Affects 3 files
🤖 Prompt for AI Agents |
||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), HTTPS_PROBE_TIMEOUT_MS); | ||
| try { | ||
| const response = await fetch(httpsUrl, { method: 'HEAD', signal: controller.signal }); | ||
| return response.ok ? httpsUrl : null; | ||
| } catch { | ||
| return null; | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| }; | ||
|
|
||
| export const getDefaultResource = ( | ||
| namespace: string, | ||
| kindRef?: K8sResourceKindReference, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: openshift/console
Length of output: 14649
🏁 Script executed:
Repository: openshift/console
Length of output: 9023
🏁 Script executed:
Repository: openshift/console
Length of output: 2336
Validate
spec.connectionConfig.urlbeforetryHttpsUpgrade.safeLoad(values.yamlData) as HelmChartRepositoryTypedoesn’t enforce runtime shape, so a non-string URL can hit.startsWith()and abort submission before the form sets an error.🤖 Prompt for AI Agents
Source: Path instructions
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.
Not a valid issue. tryHttpsUpgrade already handles missing/invalid input safely: line 78 checks if (!httpUrl?.startsWith('http://')) and returns null for undefined, null, empty string, or any non-HTTP URL. There's no crash path. Adding a separate string-type guard at the call site would be redundant defensive code.
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.
🧩 Analysis chain
🏁 Script executed:
Length of output: 4959
@sowmya-sl, the issue is still valid.httpUrl?.startsWith('http://')safely handlesnullandundefined. It does not safely handle non-null, non-string YAML values.For example, this valid YAML syntax passes through
safeLoad:safeLoad(...) as HelmChartRepositoryTypeonly changes the TypeScript view. It does not validate the runtime value. In this case,tryHttpsUpgrade(123)evaluates123?.startsWith(...)and throws because123has nostartsWithmethod.The helper should check
typeof httpUrl === 'string'before callingstartsWith, or the submission flow should validate the value before calling the helper.You are interacting with an AI system.