From 9249edf493a6df639e69ac53ec3afe5f3448c241 Mon Sep 17 00:00:00 2001 From: dvankeke Date: Wed, 19 Aug 2026 10:54:24 +0200 Subject: [PATCH 1/5] feat: add validation for Kubernetes label names and values in network policies --- src/api-v2.authz.test.ts | 60 ++++++++++++++++++++++++++++++++++++ src/openapi/definitions.yaml | 8 +++++ src/openapi/netpol.yaml | 8 ++--- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/api-v2.authz.test.ts b/src/api-v2.authz.test.ts index 272204a6a..164fc6c52 100644 --- a/src/api-v2.authz.test.ts +++ b/src/api-v2.authz.test.ts @@ -1145,6 +1145,24 @@ describe('API V2 authz tests', () => { ingress: { mode: 'AllowAll' }, }, }) + const createIngressAllowOnlyNetpol = (fromLabelValue: string, toLabelValue = 'frontend') => + createTeamResource('AplTeamNetworkControl', { + ruleType: { + type: 'ingress', + ingress: { + mode: 'AllowOnly', + toLabelName: 'app', + toLabelValue, + allow: [ + { + fromNamespace: 'team1', + fromLabelName: 'app', + fromLabelValue, + }, + ], + }, + }, + }) describe('Platform Admin', () => { test('platform admin can get all network policies', async () => { @@ -1161,6 +1179,48 @@ describe('API V2 authz tests', () => { .expect(200) }) + test('rejects multiline network policy label values', async () => { + const netpol = createIngressAllowOnlyNetpol('foo\nbar') + + const response = await agent + .post('/v2/teams/team1/netpols') + .send(netpol) + .set('Authorization', `Bearer ${teamMemberToken}`) + + expect(response.status).toBe(400) + }) + + it.each(['foo\nbar', 'foo\r\nbar', '{{ malicious }}', 'foo: bar', 'foo bar', '---'])( + 'rejects invalid label value %p', + async (fromLabelValue) => { + const response = await agent + .post('/v2/teams/team1/netpols') + .send(createIngressAllowOnlyNetpol(fromLabelValue)) + .set('Authorization', `Bearer ${teamMemberToken}`) + + expect(response.status).toBe(400) + }, + ) + + it.each(['app', 'my-app', 'my_app', 'my.app', 'APP123', ''])( + 'accepts Kubernetes label value %p', + async (fromLabelValue) => { + await agent + .post('/v2/teams/team1/netpols') + .send(createIngressAllowOnlyNetpol(fromLabelValue)) + .set('Authorization', `Bearer ${teamMemberToken}`) + .expect(200) + }, + ) + + test('rejects invalid to-label value on ingress selector', async () => { + await agent + .post('/v2/teams/team1/netpols') + .send(createIngressAllowOnlyNetpol('frontend', 'foo\nbar')) + .set('Authorization', `Bearer ${teamMemberToken}`) + .expect(400) + }) + test('team member can get team network policies', async () => { await agent.get('/v2/teams/team1/netpols').set('Authorization', `Bearer ${teamMemberToken}`).expect(200) }) diff --git a/src/openapi/definitions.yaml b/src/openapi/definitions.yaml index 763ddee6f..2251943bc 100644 --- a/src/openapi/definitions.yaml +++ b/src/openapi/definitions.yaml @@ -843,6 +843,14 @@ kms: labels: $ref: '#/annotations' description: A set of labels. +kubernetesLabelName: + type: string + maxLength: 63 + pattern: '^[A-Za-z0-9](?:[-._A-Za-z0-9]{0,61}[A-Za-z0-9])?$' +kubernetesLabelValue: + type: string + maxLength: 63 + pattern: '^$|^[A-Za-z0-9](?:[-._A-Za-z0-9]{0,61}[A-Za-z0-9])?$' logLevel: title: Log Level type: string diff --git a/src/openapi/netpol.yaml b/src/openapi/netpol.yaml index e1012e15b..accc8c272 100644 --- a/src/openapi/netpol.yaml +++ b/src/openapi/netpol.yaml @@ -52,12 +52,12 @@ ruleType: type: object properties: toLabelName: - type: string + $ref: 'definitions.yaml#/kubernetesLabelName' title: Selector label name description: 'The name of the Pod selector label.' example: app toLabelValue: - type: string + $ref: 'definitions.yaml#/kubernetesLabelValue' title: Selector label value description: 'The value of the Pod selector label.' example: my-app @@ -79,12 +79,12 @@ ruleType: title: Namespace name description: 'The name of the namespace.' fromLabelName: - type: string + $ref: 'definitions.yaml#/kubernetesLabelName' title: Selector label name description: 'The name of the Pod selector label.' example: app fromLabelValue: - type: string + $ref: 'definitions.yaml#/kubernetesLabelValue' title: Selector label value description: 'The value of the Pod selector label.' example: my-app From 5acb2c61e910588beae32735159e7da3b75cecc1 Mon Sep 17 00:00:00 2001 From: dvankeke Date: Wed, 19 Aug 2026 11:03:29 +0200 Subject: [PATCH 2/5] fix: redos test coverage --- src/patterns.test.ts | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/patterns.test.ts b/src/patterns.test.ts index e52af9fdc..18029adf7 100644 --- a/src/patterns.test.ts +++ b/src/patterns.test.ts @@ -104,6 +104,10 @@ const redosCases: Record = { k8sName: [`${'a-'.repeat(50_000)}!`], + kubernetesLabelValue: [`${'a-'.repeat(50_000)}!`, `${'a.'.repeat(50_000)}!`], + + kubernetesLabelName: [`${'a-'.repeat(50_000)}!`, `${'a.'.repeat(50_000)}!`], + idName: [`${'a-'.repeat(50_000)}!`], settingsName: [`${'aB'.repeat(50_000)}!`, `${'a-'.repeat(50_000)}!`], @@ -147,6 +151,8 @@ describe('OpenAPI definition regex patterns', () => { 'email', 'hostPort', 'k8sName', + 'kubernetesLabelValue', + 'kubernetesLabelName', 'idName', 'quantityCpu', 'quantityMem', @@ -242,6 +248,55 @@ describe('OpenAPI definition regex patterns', () => { }) }) + describe('kubernetesLabelValue', () => { + it('accepts valid Kubernetes label values', () => { + expectValid('kubernetesLabelValue', ['', 'app', 'my-app', 'my_app', 'my.app', 'APP123', 'a'.repeat(63)]) + }) + + it('rejects invalid Kubernetes label values', () => { + expectInvalid('kubernetesLabelValue', [ + '-app', + 'app-', + '.app', + 'app.', + '_app', + 'app_', + 'app value', + 'foo\nbar', + 'foo\r\nbar', + '{{ malicious }}', + 'foo: bar', + '---', + 'a'.repeat(64), + ]) + }) + }) + + describe('kubernetesLabelName', () => { + it('accepts valid Kubernetes label names', () => { + expectValid('kubernetesLabelName', ['app', 'my-app', 'my_app', 'my.app', 'APP123', 'a'.repeat(63)]) + }) + + it('rejects invalid Kubernetes label names', () => { + expectInvalid('kubernetesLabelName', [ + '', + '-app', + 'app-', + '.app', + 'app.', + '_app', + 'app_', + 'app name', + 'foo\nbar', + 'foo\r\nbar', + '{{ malicious }}', + 'foo: bar', + '---', + 'a'.repeat(64), + ]) + }) + }) + describe('idName', () => { it('accepts valid ID names', () => { expectValid('idName', ['a', 'secret', 'secret-name', 'secret1']) From da981791e7588717fb89ee482c69dddefcdc99ac Mon Sep 17 00:00:00 2001 From: Dennis van Kekem <38350840+dennisvankekem@users.noreply.github.com> Date: Thu, 20 Aug 2026 13:33:42 +0200 Subject: [PATCH 3/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/openapi/definitions.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openapi/definitions.yaml b/src/openapi/definitions.yaml index 2251943bc..375d8ff27 100644 --- a/src/openapi/definitions.yaml +++ b/src/openapi/definitions.yaml @@ -846,11 +846,11 @@ labels: kubernetesLabelName: type: string maxLength: 63 - pattern: '^[A-Za-z0-9](?:[-._A-Za-z0-9]{0,61}[A-Za-z0-9])?$' + pattern: '^(?![\s\S]*[\r\n])[A-Za-z0-9](?:[-._A-Za-z0-9]{0,61}[A-Za-z0-9])?$' kubernetesLabelValue: type: string maxLength: 63 - pattern: '^$|^[A-Za-z0-9](?:[-._A-Za-z0-9]{0,61}[A-Za-z0-9])?$' + pattern: '^(?:$|(?![\s\S]*[\r\n])[A-Za-z0-9](?:[-._A-Za-z0-9]{0,61}[A-Za-z0-9])?)$' logLevel: title: Log Level type: string From 4dfdd1f9cbc6a068c2b0eb1a3c24dd4cefb95d49 Mon Sep 17 00:00:00 2001 From: Dennis van Kekem <38350840+dennisvankekem@users.noreply.github.com> Date: Thu, 20 Aug 2026 13:33:48 +0200 Subject: [PATCH 4/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/patterns.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/patterns.test.ts b/src/patterns.test.ts index 18029adf7..6a13152d5 100644 --- a/src/patterns.test.ts +++ b/src/patterns.test.ts @@ -264,6 +264,8 @@ describe('OpenAPI definition regex patterns', () => { 'app value', 'foo\nbar', 'foo\r\nbar', + 'app\n', + 'app\r\n', '{{ malicious }}', 'foo: bar', '---', From 0418acb4221d929412c783fc6b446261dab037a0 Mon Sep 17 00:00:00 2001 From: Dennis van Kekem <38350840+dennisvankekem@users.noreply.github.com> Date: Thu, 20 Aug 2026 13:34:02 +0200 Subject: [PATCH 5/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/patterns.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/patterns.test.ts b/src/patterns.test.ts index 6a13152d5..982063114 100644 --- a/src/patterns.test.ts +++ b/src/patterns.test.ts @@ -291,6 +291,8 @@ describe('OpenAPI definition regex patterns', () => { 'app name', 'foo\nbar', 'foo\r\nbar', + 'app\n', + 'app\r\n', '{{ malicious }}', 'foo: bar', '---',