Compare commits

...

3 Commits

Author SHA1 Message Date
github-actions[bot]
759bd2888d Bump version to 2.9.8 (#17514)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: crenshaw-dev <crenshaw-dev@users.noreply.github.com>
2024-03-13 14:45:51 -04:00
Michael Crenshaw
2a410f3441 Merge pull request from GHSA-g623-jcgg-mhmm
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2024-03-13 14:28:43 -04:00
Alexandre Gaudreault
689a73a729 Merge pull request from GHSA-jwv5-8mqv-g387
* fix: Validate external URLs for applicatins

Signed-off-by: Ry0taK <49341894+Ry0taK@users.noreply.github.com>

* fix(ui): remove invalid external-link

Signed-off-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>

* linting

Signed-off-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>

---------

Signed-off-by: Ry0taK <49341894+Ry0taK@users.noreply.github.com>
Signed-off-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
Co-authored-by: Ry0taK <49341894+Ry0taK@users.noreply.github.com>
2024-03-13 14:26:47 -04:00
14 changed files with 155 additions and 49 deletions

View File

@@ -1 +1 @@
2.9.7
2.9.8

View File

@@ -5,7 +5,7 @@ kind: Kustomization
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.9.7
newTag: v2.9.8
resources:
- ./application-controller
- ./dex

View File

@@ -20750,7 +20750,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -21050,7 +21050,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -21102,7 +21102,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -21321,7 +21321,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -12,4 +12,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.9.7
newTag: v2.9.8

View File

@@ -12,7 +12,7 @@ patches:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.9.7
newTag: v2.9.8
resources:
- ../../base/application-controller
- ../../base/applicationset-controller

View File

@@ -22007,7 +22007,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -22130,7 +22130,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -22206,7 +22206,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -22537,7 +22537,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -22589,7 +22589,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -22884,7 +22884,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -23130,7 +23130,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -1662,7 +1662,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1785,7 +1785,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1861,7 +1861,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -2192,7 +2192,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2244,7 +2244,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2539,7 +2539,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2785,7 +2785,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -21102,7 +21102,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -21225,7 +21225,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -21301,7 +21301,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -21583,7 +21583,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -21635,7 +21635,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -21928,7 +21928,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -22174,7 +22174,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -757,7 +757,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -880,7 +880,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -956,7 +956,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1238,7 +1238,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1290,7 +1290,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1583,7 +1583,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1829,7 +1829,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.7
image: quay.io/argoproj/argocd:v2.9.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -325,6 +325,15 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
return nil, security.NamespaceNotPermittedError(appNs)
}
// Don't let the app creator set the operation explicitly. Those requests should always go through the Sync API.
if a.Operation != nil {
log.WithFields(log.Fields{
"application": a.Name,
argocommon.SecurityField: argocommon.SecurityLow,
}).Warn("User attempted to set operation on application creation. This could have allowed them to bypass branch protection rules by setting manifests directly. Ignoring the set operation.")
a.Operation = nil
}
created, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Create(ctx, a, metav1.CreateOptions{})
if err == nil {
s.logAppEvent(created, ctx, argo.EventReasonResourceCreated, "created application")

View File

@@ -1439,6 +1439,27 @@ func TestCreateAppWithDestName(t *testing.T) {
assert.Equal(t, app.Spec.Destination.Server, "https://cluster-api.com")
}
// TestCreateAppWithOperation tests that an application created with an operation is created with the operation removed.
// Avoids regressions of https://github.com/argoproj/argo-cd/security/advisories/GHSA-g623-jcgg-mhmm
func TestCreateAppWithOperation(t *testing.T) {
appServer := newTestAppServer(t)
testApp := newTestAppWithDestName()
testApp.Operation = &appsv1.Operation{
Sync: &appsv1.SyncOperation{
Manifests: []string{
"test",
},
},
}
createReq := application.ApplicationCreateRequest{
Application: testApp,
}
app, err := appServer.Create(context.Background(), &createReq)
require.NoError(t, err)
require.NotNil(t, app)
assert.Nil(t, app.Operation)
}
func TestUpdateApp(t *testing.T) {
testApp := newTestApp()
appServer := newTestAppServer(t, testApp)

View File

@@ -30,6 +30,7 @@ import {EditAnnotations} from './edit-annotations';
import './application-summary.scss';
import {DeepLinks} from '../../../shared/components/deep-links';
import {ExternalLinks} from '../application-urls';
function swap(array: any[], a: number, b: number) {
array = array.slice();
@@ -326,20 +327,19 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
)
}
];
const urls = app.status.summary.externalURLs || [];
const urls = ExternalLinks(app.status.summary.externalURLs);
if (urls.length > 0) {
attributes.push({
title: 'URLs',
view: (
<React.Fragment>
{urls
.map(item => item.split('|'))
.map((parts, i) => (
<a key={i} href={parts.length > 1 ? parts[1] : parts[0]} target='__blank'>
{parts[0]} &nbsp;
{urls.map((url, i) => {
return (
<a key={i} href={url.ref} target='__blank'>
{url.title} &nbsp;
</a>
))}
);
})}
</React.Fragment>
)
});

View File

@@ -1,4 +1,4 @@
import {ExternalLink, InvalidExternalLinkError} from './application-urls';
import { ExternalLink, ExternalLinks, InvalidExternalLinkError } from './application-urls';
test('rejects malicious URLs', () => {
expect(() => {
@@ -7,6 +7,16 @@ test('rejects malicious URLs', () => {
expect(() => {
const _ = new ExternalLink('data:text/html;<h1>hi</h1>');
}).toThrowError(InvalidExternalLinkError);
expect(() => {
const _ = new ExternalLink('title|data:text/html;<h1>hi</h1>');
}).toThrowError(InvalidExternalLinkError);
expect(() => {
const _ = new ExternalLink('data:title|data:text/html;<h1>hi</h1>');
}).toThrowError(InvalidExternalLinkError);
expect(() => {
const _ = new ExternalLink('data:title|https://localhost:8080/applications');
}).not.toThrowError(InvalidExternalLinkError);
});
test('allows absolute URLs', () => {
@@ -18,3 +28,59 @@ test('allows relative URLs', () => {
window.location = new URL('https://localhost:8080/applications');
expect(new ExternalLink('/applications').ref).toEqual('/applications');
});
test('URLs format', () => {
expect(new ExternalLink('https://localhost:8080/applications')).toEqual({
ref: 'https://localhost:8080/applications',
title: 'https://localhost:8080/applications',
})
expect(new ExternalLink('title|https://localhost:8080/applications')).toEqual({
ref: 'https://localhost:8080/applications',
title: 'title',
})
});
test('malicious URLs from list to be removed', () => {
const urls: string[] = [
'javascript:alert("hi")',
'https://localhost:8080/applications',
]
const links = ExternalLinks(urls);
expect(links).toHaveLength(1);
expect(links).toContainEqual({
ref: 'https://localhost:8080/applications',
title: 'https://localhost:8080/applications',
});
});
test('list to be sorted', () => {
const urls: string[] = [
'https://a',
'https://b',
'a|https://c',
'z|https://c',
'x|https://d',
'x|https://c',
]
const links = ExternalLinks(urls);
// 'a|https://c',
// 'x|https://c',
// 'x|https://d',
// 'z|https://c',
// 'https://a',
// 'https://b',
expect(links).toHaveLength(6);
expect(links[0].title).toEqual('a')
expect(links[1].title).toEqual('x')
expect(links[1].ref).toEqual('https://c')
expect(links[2].title).toEqual('x')
expect(links[2].ref).toEqual('https://d')
expect(links[3].title).toEqual('z')
expect(links[4].title).toEqual('https://a')
expect(links[5].title).toEqual('https://b')
});

View File

@@ -29,7 +29,7 @@ export class ExternalLink {
}
}
export const ApplicationURLs = ({urls}: {urls: string[]}) => {
export const ExternalLinks = (urls?: string[]) => {
const externalLinks: ExternalLink[] = [];
for (const url of urls || []) {
try {
@@ -42,16 +42,26 @@ export const ApplicationURLs = ({urls}: {urls: string[]}) => {
// sorted alphabetically & links with titles first
externalLinks.sort((a, b) => {
if (a.title !== '' && b.title !== '') {
const hasTitle = (x: ExternalLink): boolean => {
return x.title !== x.ref && x.title !== '';
};
if (hasTitle(a) && hasTitle(b) && a.title !== b.title) {
return a.title > b.title ? 1 : -1;
} else if (a.title === '') {
} else if (hasTitle(b) && !hasTitle(a)) {
return 1;
} else if (b.title === '') {
} else if (hasTitle(a) && !hasTitle(b)) {
return -1;
}
return a.ref > b.ref ? 1 : -1;
});
return externalLinks;
};
export const ApplicationURLs = ({urls}: {urls: string[]}) => {
const externalLinks: ExternalLink[] = ExternalLinks(urls);
return (
((externalLinks || []).length > 0 && (
<div className='applications-list__external-links-icon-container'>