Add CMMI maturity level to compliance controls
Adds an optional CMMI 0-5 maturity level field to Control to support ISO 27001 clause 9.1 effectiveness measurement and HITRUST CSF maturity requirements. The field is nullable, framework-agnostic, and exposed across all four API surfaces (GraphQL, MCP, CLI, n8n) plus the generated SoA document. Signed-off-by: Alejandro Juan <alejandrojuan@alejandrojuan.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
98487953b9
commit
da91afc2a7
43
packages/helpers/src/controls.ts
Normal file
43
packages/helpers/src/controls.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
type Translator = (s: string) => string;
|
||||
|
||||
export const controlMaturityLevels = [
|
||||
"NONE",
|
||||
"INITIAL",
|
||||
"MANAGED",
|
||||
"DEFINED",
|
||||
"QUANTITATIVELY_MANAGED",
|
||||
"OPTIMIZING",
|
||||
] as const;
|
||||
|
||||
export type ControlMaturityLevel = (typeof controlMaturityLevels)[number];
|
||||
|
||||
export function getControlMaturityLevelLabel(__: Translator, level: string) {
|
||||
switch (level) {
|
||||
case "NONE":
|
||||
return __("0 - None");
|
||||
case "INITIAL":
|
||||
return __("1 - Initial");
|
||||
case "MANAGED":
|
||||
return __("2 - Managed");
|
||||
case "DEFINED":
|
||||
return __("3 - Defined");
|
||||
case "QUANTITATIVELY_MANAGED":
|
||||
return __("4 - Quantitatively Managed");
|
||||
case "OPTIMIZING":
|
||||
return __("5 - Optimizing");
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,11 @@ export {
|
||||
documentWriteModes,
|
||||
getDocumentWriteModeLabel,
|
||||
} from "./documents";
|
||||
export {
|
||||
controlMaturityLevels,
|
||||
getControlMaturityLevelLabel,
|
||||
type ControlMaturityLevel,
|
||||
} from "./controls";
|
||||
export { getAssetTypeVariant } from "./assets";
|
||||
export {
|
||||
getSnapshotTypeLabel,
|
||||
|
||||
@@ -71,6 +71,28 @@ export const description: INodeProperties[] = [
|
||||
default: '',
|
||||
description: 'The description of the control',
|
||||
},
|
||||
{
|
||||
displayName: 'Maturity Level',
|
||||
name: 'maturityLevel',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['control'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{ name: '0 - None', value: 'NONE' },
|
||||
{ name: '1 - Initial', value: 'INITIAL' },
|
||||
{ name: '2 - Managed', value: 'MANAGED' },
|
||||
{ name: '3 - Defined', value: 'DEFINED' },
|
||||
{ name: '4 - Quantitatively Managed', value: 'QUANTITATIVELY_MANAGED' },
|
||||
{ name: '5 - Optimizing', value: 'OPTIMIZING' },
|
||||
{ name: 'Not Set', value: '' },
|
||||
],
|
||||
default: '',
|
||||
description: 'CMMI 0-5 maturity level (optional)',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
@@ -81,6 +103,7 @@ export async function execute(
|
||||
const sectionTitle = this.getNodeParameter('sectionTitle', itemIndex) as string;
|
||||
const name = this.getNodeParameter('name', itemIndex) as string;
|
||||
const description = this.getNodeParameter('description', itemIndex, '') as string;
|
||||
const maturityLevel = this.getNodeParameter('maturityLevel', itemIndex, '') as string;
|
||||
|
||||
const query = `
|
||||
mutation CreateControl($input: CreateControlInput!) {
|
||||
@@ -91,6 +114,7 @@ export async function execute(
|
||||
sectionTitle
|
||||
name
|
||||
description
|
||||
maturityLevel
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
@@ -106,6 +130,7 @@ export async function execute(
|
||||
name,
|
||||
bestPractice: true,
|
||||
...(description && { description }),
|
||||
...(maturityLevel && { maturityLevel }),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ export async function execute(
|
||||
sectionTitle
|
||||
name
|
||||
description
|
||||
implemented
|
||||
notImplementedJustification
|
||||
maturityLevel
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ export async function execute(
|
||||
sectionTitle
|
||||
name
|
||||
description
|
||||
implemented
|
||||
maturityLevel
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
|
||||
@@ -69,6 +69,29 @@ export const description: INodeProperties[] = [
|
||||
default: '',
|
||||
description: 'The description of the control',
|
||||
},
|
||||
{
|
||||
displayName: 'Maturity Level',
|
||||
name: 'maturityLevel',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['control'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{ name: '(Unchanged)', value: '' },
|
||||
{ name: '0 - None', value: 'NONE' },
|
||||
{ name: '1 - Initial', value: 'INITIAL' },
|
||||
{ name: '2 - Managed', value: 'MANAGED' },
|
||||
{ name: '3 - Defined', value: 'DEFINED' },
|
||||
{ name: '4 - Quantitatively Managed', value: 'QUANTITATIVELY_MANAGED' },
|
||||
{ name: '5 - Optimizing', value: 'OPTIMIZING' },
|
||||
{ name: 'Not Set', value: '__unset__' },
|
||||
],
|
||||
default: '',
|
||||
description: 'CMMI 0-5 maturity level. Choose "Not set" to clear the value.',
|
||||
},
|
||||
];
|
||||
|
||||
export async function execute(
|
||||
@@ -79,6 +102,7 @@ export async function execute(
|
||||
const sectionTitle = this.getNodeParameter('sectionTitle', itemIndex, '') as string;
|
||||
const name = this.getNodeParameter('name', itemIndex, '') as string;
|
||||
const description = this.getNodeParameter('description', itemIndex, '') as string;
|
||||
const maturityLevel = this.getNodeParameter('maturityLevel', itemIndex, '') as string;
|
||||
|
||||
const query = `
|
||||
mutation UpdateControl($input: UpdateControlInput!) {
|
||||
@@ -88,6 +112,7 @@ export async function execute(
|
||||
sectionTitle
|
||||
name
|
||||
description
|
||||
maturityLevel
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
@@ -95,10 +120,15 @@ export async function execute(
|
||||
}
|
||||
`;
|
||||
|
||||
const input: Record<string, string> = { id };
|
||||
const input: Record<string, string | null> = { id };
|
||||
if (sectionTitle) input.sectionTitle = sectionTitle;
|
||||
if (name) input.name = name;
|
||||
if (description) input.description = description;
|
||||
if (maturityLevel === '__unset__') {
|
||||
input.maturityLevel = null;
|
||||
} else if (maturityLevel) {
|
||||
input.maturityLevel = maturityLevel;
|
||||
}
|
||||
|
||||
const responseData = await proboApiRequest.call(this, query, { input });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user