Skip to content

Commit

Permalink
fix: fix no pod create/execute rules
Browse files Browse the repository at this point in the history
  • Loading branch information
WitoDelnat committed Oct 11, 2023
1 parent 93306eb commit f416729
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 4 deletions.
70 changes: 70 additions & 0 deletions packages/validation/src/__tests__/MonokleValidator.kbp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {expect, it} from 'vitest';
import {MonokleValidator} from '../MonokleValidator.js';
import {processRefs} from '../references/index.js';

// Usage note: This library relies on fetch being on global scope!
import 'isomorphic-fetch';
import {extractK8sResources} from '@monokle/parser';
import {ValidationConfig} from '@monokle/types';
import {ResourceParser} from '../common/resourceParser.js';
import {Config, RuleMap} from '../config/parse.js';
import {readDirectory} from './testUtils.js';
import {DefaultPluginLoader} from '../pluginLoaders/PluginLoader.js';
import {SchemaLoader} from '../validators/index.js';
import {DisabledFixer} from '../sarif/index.js';

it('should detect rules which allow creation of pods', async () => {
const {response} = await processResourcesInFolder('src/__tests__/resources/kbp', {
'practices/no-pod-create': true,
'practices/no-pod-execute': false,
});

const errorCount = response.runs.reduce((sum, r) => sum + r.results.length, 0);
expect(errorCount).toBe(2);
});

it('should detect rules which allow execution of pods', async () => {
const {response} = await processResourcesInFolder('src/__tests__/resources/kbp', {
'practices/no-pod-create': false,
'practices/no-pod-execute': true,
});

const errorCount = response.runs.reduce((sum, r) => sum + r.results.length, 0);
expect(errorCount).toBe(1);
});

async function processResourcesInFolder(path: string, rules?: RuleMap) {
const files = await readDirectory(path);
const resources = extractK8sResources(files);

const parser = new ResourceParser();
const validator = createTestValidator(parser, rules);
const response = await validator.validate({resources});
return {response, resources};
}

function createTestValidator(parser: ResourceParser, rules?: ValidationConfig['rules']) {
const config: Config = {
plugins: {
practices: true,
},
settings: {
debug: true,
},
};

if (rules) {
config.rules = rules;
}

return new MonokleValidator(
{
loader: new DefaultPluginLoader(),
parser,
schemaLoader: new SchemaLoader(),
suppressors: [],
fixer: new DisabledFixer(),
},
config
);
}
88 changes: 88 additions & 0 deletions packages/validation/src/__tests__/resources/kbp/KBP107-role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: argocd-applicationset-controller
namespace: argocd
labels:
app.kubernetes.io/component: applicationset-controller
app.kubernetes.io/name: argocd-applicationset-controller
app.kubernetes.io/part-of: argocd
rules:
- apiGroups:
- argoproj.io
resources:
- applications
- applicationsets
- applicationsets/finalizers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- argoproj.io
resources:
- appprojects
verbs:
- get
- apiGroups:
- argoproj.io
resources:
- applicationsets/status
verbs:
- get
- patch
- update
- apiGroups:
- argoproj.io
resources:
- applicationsets/status
verbs:
- get
- patch
- update
- apiGroups:
- ""
resources:
- events
verbs:
- create
- get
- list
- patch
- watch
- apiGroups:
- ""
resources:
- secrets
- configmaps
verbs:
- get
- list
- watch
- apiGroups:
- apps
- extensions
resources:
- deployments
verbs:
- get
- create
- list
- watch
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- create
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {isClusterRole} from '../../custom/schemas/clusterrole.rbac.authorization
import {isRole} from '../../custom/schemas/role.rbac.authorization.k8s.io.v1.js';
import {NSA_RELATIONS} from '../../../taxonomies/nsa.js';

const KIND_CREATES_PODS = ['pods', 'deployments', 'statefulsets', 'jobs', 'cronjobs'];

export const noPodCreate = defineRule({
id: 107,
description: 'Disallow permissions to create pods',
Expand All @@ -15,8 +17,10 @@ export const noPodCreate = defineRule({
validate({resources}, {report}) {
resources.filter(isTarget).forEach((role, index) => {
role.rules?.forEach(rule => {
const isValid = rule.verbs?.includes('create');
if (isValid) return;
const createPermission = rule.verbs?.includes('create');
const resourceCanCreatePod = rule.resources?.some(r => KIND_CREATES_PODS.includes(r.toLowerCase()));
const isInvalid = resourceCanCreatePod && createPermission;
if (!isInvalid) return;
report(role, {path: `spec.rules.${index}.verbs`});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import {NSA_RELATIONS} from '../../../taxonomies/nsa.js';
export const noPodExecute = defineRule({
id: 108,
description: 'Disallow permissions to exec on pods',
help: "Do not include 'pods/exec' in 'spec.rules[x].resourcess",
help: "Do not include 'pods/exec' in 'spec.rules[x].resources",
advanced: {
severity: 5,
relationships: [NSA_RELATIONS['kubernetes-pod-security']],
},
validate({resources}, {report}) {
resources.filter(isTarget).forEach((role, index) => {
role.rules?.forEach(rule => {
const isValid = rule.resources?.includes('pods/exec');
const isValid = rule.resources?.every(r => r.toLowerCase() !== 'pods/exec');
if (isValid) return;
report(role, {path: `spec.rules.${index}.resources`});
});
Expand Down

0 comments on commit f416729

Please sign in to comment.