@aifabrix/builder 2.45.0 → 2.45.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.projects.js +44 -1
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +45 -2
- package/lib/api/pipeline.api.js +4 -4
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +30 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +3 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy.js +34 -19
- package/lib/cli/index.js +4 -2
- package/lib/cli/infra-guided.js +22 -3
- package/lib/cli/setup-app.help.js +50 -2
- package/lib/cli/setup-app.js +33 -24
- package/lib/cli/setup-app.test-commands.js +80 -21
- package/lib/cli/setup-auth.js +30 -3
- package/lib/cli/setup-external-system.js +37 -3
- package/lib/cli/setup-utility-help-after.js +45 -0
- package/lib/cli/setup-utility-resolve.js +2 -2
- package/lib/cli/setup-utility.js +99 -81
- package/lib/commands/auth-status-dataplane-version.js +231 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +70 -6
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +114 -0
- package/lib/commands/datasource-unified-test-cli.js +25 -17
- package/lib/commands/datasource-unified-test-cli.options.js +18 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/login.js +46 -16
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair.js +13 -3
- package/lib/commands/test-e2e-external.js +40 -9
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-command-action.js +55 -0
- package/lib/commands/test-governance-external.js +88 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-command-action.js +118 -0
- package/lib/commands/test-trust-external.js +127 -0
- package/lib/commands/upload.js +34 -13
- package/lib/commands/wizard-core.js +3 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +93 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +103 -0
- package/lib/datasource/audit-evidence-extract.js +177 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +88 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +92 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +26 -2
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +43 -17
- package/lib/datasource/validate.js +37 -20
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +3 -1
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +119 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/protection/auth-context.js +45 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/external-datasource.schema.json +47 -13
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +636 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/help-builder.js +30 -24
- package/lib/utils/manifest-source-emit.js +22 -0
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/upload-pipeline-ui.js +91 -0
- package/lib/utils/validation-poll-ui.js +14 -7
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +1 -1
- package/templates/applications/dataplane/env.template +13 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/external-system/README.md.hbs +56 -8
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Helpers for audit evidence matrix verification (sub-resource validators, rows 8–9)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { checkPerExecutionRow } = require('./audit-evidence-matrix-rows');
|
|
10
|
+
const {
|
|
11
|
+
normalizeAuditOperation,
|
|
12
|
+
executionsCoverExpectedOperations
|
|
13
|
+
} = require('./audit-evidence-operation-aliases');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} input
|
|
17
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
18
|
+
*/
|
|
19
|
+
function checkRow8CorrelationGroup(input) {
|
|
20
|
+
const row = { row: 8, status: 'failed', detail: '', code: 'correlationGroup' };
|
|
21
|
+
const items = input.executions || [];
|
|
22
|
+
if (!items.length) {
|
|
23
|
+
return {
|
|
24
|
+
...row,
|
|
25
|
+
detail: input.correlationId
|
|
26
|
+
? 'no executions for correlation or datasource filter'
|
|
27
|
+
: 'no correlationId and no executions',
|
|
28
|
+
code: 'correlationEmpty'
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const expected = (input.expectedOperations || [])
|
|
32
|
+
.map(normalizeAuditOperation)
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
if (expected.length && !executionsCoverExpectedOperations(items, expected)) {
|
|
35
|
+
const ops = new Set(
|
|
36
|
+
items.map(e => normalizeAuditOperation(e && e.operation)).filter(Boolean)
|
|
37
|
+
);
|
|
38
|
+
const missing = expected.filter(op => !ops.has(op));
|
|
39
|
+
return {
|
|
40
|
+
...row,
|
|
41
|
+
detail: `missing ops: ${missing.join(', ')}`,
|
|
42
|
+
code: 'correlationOpsMissing'
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const opCount = new Set(
|
|
46
|
+
items.map(e => normalizeAuditOperation(e && e.operation)).filter(Boolean)
|
|
47
|
+
).size;
|
|
48
|
+
return {
|
|
49
|
+
...row,
|
|
50
|
+
status: 'passed',
|
|
51
|
+
detail: `correlation ops=${opCount || items.length}`,
|
|
52
|
+
code: 'correlationGroup'
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Prefer executions tied to this run (by id), then correlation query, then datasource list.
|
|
58
|
+
* @param {Object} ctx
|
|
59
|
+
* @param {Array} executions
|
|
60
|
+
* @param {Array} corrQueryRows
|
|
61
|
+
* @returns {Array}
|
|
62
|
+
*/
|
|
63
|
+
function resolveRow8Executions(ctx, executions, corrQueryRows) {
|
|
64
|
+
const idSet = new Set(
|
|
65
|
+
(ctx.executionIds || []).map(id => String(id).trim()).filter(Boolean)
|
|
66
|
+
);
|
|
67
|
+
let byId = [];
|
|
68
|
+
if (idSet.size && executions && executions.length) {
|
|
69
|
+
byId = executions.filter(e => e && e.id && idSet.has(String(e.id)));
|
|
70
|
+
}
|
|
71
|
+
const expected = ctx.expectedOperations || [];
|
|
72
|
+
if (byId.length && executionsCoverExpectedOperations(byId, expected)) {
|
|
73
|
+
return byId;
|
|
74
|
+
}
|
|
75
|
+
if (corrQueryRows && corrQueryRows.length && executionsCoverExpectedOperations(corrQueryRows, expected)) {
|
|
76
|
+
return corrQueryRows;
|
|
77
|
+
}
|
|
78
|
+
if (byId.length) return byId;
|
|
79
|
+
if (corrQueryRows && corrQueryRows.length) return corrQueryRows;
|
|
80
|
+
return executions || [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {Set<string>} traceIds
|
|
85
|
+
* @param {string[]} execIds
|
|
86
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
87
|
+
*/
|
|
88
|
+
function checkRow9Traces(traceIds, execIds) {
|
|
89
|
+
const row = { row: 9, status: 'failed', detail: '', code: 'traceMismatch' };
|
|
90
|
+
if (!execIds.length) {
|
|
91
|
+
return { ...row, detail: 'no execution ids', code: 'auditIdsMissing' };
|
|
92
|
+
}
|
|
93
|
+
if (traceIds.size < 1) {
|
|
94
|
+
return { ...row, detail: 'no trace payloads resolved', code: 'traceEmpty' };
|
|
95
|
+
}
|
|
96
|
+
if (traceIds.size < execIds.length) {
|
|
97
|
+
return {
|
|
98
|
+
...row,
|
|
99
|
+
detail: `distinct traces=${traceIds.size} executionIds=${execIds.length}`,
|
|
100
|
+
code: 'traceCountLow'
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return { ...row, status: 'passed', detail: `traces=${traceIds.size} ids=${execIds.length}` };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isRbacSubResourceValid(body) {
|
|
107
|
+
return !!(body && typeof body === 'object' && (body.id || body.decision || body.summary));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isAbacSubResourceValid(body) {
|
|
111
|
+
return !!(body && typeof body === 'object' && (body.id || body.decision || body.traceId));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isErrorsValid(body) {
|
|
115
|
+
if (Array.isArray(body)) return true;
|
|
116
|
+
if (body && typeof body === 'object' && Array.isArray(body.data)) return true;
|
|
117
|
+
return body && typeof body === 'object';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isStepsValid(body) {
|
|
121
|
+
if (Array.isArray(body) && body.length > 0) return true;
|
|
122
|
+
if (body && Array.isArray(body.steps) && body.steps.length > 0) return true;
|
|
123
|
+
return body && typeof body === 'object' && Object.keys(body).length > 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {Array} executions
|
|
128
|
+
* @param {string[]} fallbackIds
|
|
129
|
+
* @returns {string[]}
|
|
130
|
+
*/
|
|
131
|
+
function resolveExecutionIdsForChecks(executions, fallbackIds) {
|
|
132
|
+
const fromRows = (executions || [])
|
|
133
|
+
.map(e => (e && e.id ? String(e.id).trim() : ''))
|
|
134
|
+
.filter(Boolean);
|
|
135
|
+
return [...new Set(fromRows.concat(fallbackIds || []))];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @param {Object} opts
|
|
140
|
+
* @returns {Promise<import('../api/types/audit.types').AuditEvidenceMatrixRow>}
|
|
141
|
+
*/
|
|
142
|
+
async function checkPerExecutionSub(opts) {
|
|
143
|
+
const { dataplaneUrl, authConfig, executionId, rowNum, label, fetcher, isValid } = opts;
|
|
144
|
+
try {
|
|
145
|
+
const body = await fetcher(dataplaneUrl, authConfig, executionId);
|
|
146
|
+
return checkPerExecutionRow(rowNum, `${label}Null`, label, body, isValid);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
return {
|
|
149
|
+
row: rowNum,
|
|
150
|
+
status: 'failed',
|
|
151
|
+
detail: `${label}: ${err.message}`,
|
|
152
|
+
code: `${label}Error`
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
checkRow8CorrelationGroup,
|
|
159
|
+
resolveRow8Executions,
|
|
160
|
+
checkRow9Traces,
|
|
161
|
+
isRbacSubResourceValid,
|
|
162
|
+
isAbacSubResourceValid,
|
|
163
|
+
isErrorsValid,
|
|
164
|
+
isStepsValid,
|
|
165
|
+
resolveExecutionIdsForChecks,
|
|
166
|
+
checkPerExecutionSub
|
|
167
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Build matrix rows 1–9 for audit evidence verification
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
queryExecutions,
|
|
11
|
+
queryRbacDecisions,
|
|
12
|
+
queryAbacTraces,
|
|
13
|
+
getExecutionRbac,
|
|
14
|
+
getExecutionAbac,
|
|
15
|
+
getExecutionErrors,
|
|
16
|
+
getExecutionSteps,
|
|
17
|
+
getExecutionTrace
|
|
18
|
+
} = require('../api/audit.api');
|
|
19
|
+
const {
|
|
20
|
+
checkRow1ExecutionsByDatasource,
|
|
21
|
+
checkRow2RbacDecisionsList,
|
|
22
|
+
checkRow3AbacTracesList
|
|
23
|
+
} = require('./audit-evidence-matrix-rows');
|
|
24
|
+
const {
|
|
25
|
+
operationQueryVariants,
|
|
26
|
+
executionsCoverExpectedOperations
|
|
27
|
+
} = require('./audit-evidence-operation-aliases');
|
|
28
|
+
const {
|
|
29
|
+
checkRow8CorrelationGroup,
|
|
30
|
+
resolveRow8Executions,
|
|
31
|
+
checkRow9Traces,
|
|
32
|
+
isRbacSubResourceValid,
|
|
33
|
+
isAbacSubResourceValid,
|
|
34
|
+
isErrorsValid,
|
|
35
|
+
isStepsValid,
|
|
36
|
+
resolveExecutionIdsForChecks,
|
|
37
|
+
checkPerExecutionSub
|
|
38
|
+
} = require('./audit-evidence-verify-helpers');
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} dataplaneUrl
|
|
42
|
+
* @param {Object} authConfig
|
|
43
|
+
* @param {string} correlationId
|
|
44
|
+
* @param {string[]} expectedOperations
|
|
45
|
+
* @returns {Promise<Array>}
|
|
46
|
+
*/
|
|
47
|
+
async function fetchCorrelationExecutionsByOperation(
|
|
48
|
+
dataplaneUrl,
|
|
49
|
+
authConfig,
|
|
50
|
+
correlationId,
|
|
51
|
+
expectedOperations
|
|
52
|
+
) {
|
|
53
|
+
const deduped = new Map();
|
|
54
|
+
for (const rawOp of expectedOperations) {
|
|
55
|
+
const variants = operationQueryVariants(rawOp);
|
|
56
|
+
for (const op of variants) {
|
|
57
|
+
const res = await queryExecutions(dataplaneUrl, authConfig, {
|
|
58
|
+
correlationId,
|
|
59
|
+
operation: op,
|
|
60
|
+
pageSize: 20
|
|
61
|
+
});
|
|
62
|
+
for (const row of res.data || []) {
|
|
63
|
+
if (row && row.id) deduped.set(String(row.id), row);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const merged = [...deduped.values()];
|
|
68
|
+
return executionsCoverExpectedOperations(merged, expectedOperations) ? merged : [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {Object} ctx
|
|
73
|
+
* @param {Object} polled
|
|
74
|
+
* @param {number} minExpected
|
|
75
|
+
* @returns {Promise<import('../api/types/audit.types').AuditEvidenceMatrixRow[]>}
|
|
76
|
+
*/
|
|
77
|
+
async function buildListMatrixRows(ctx, polled, minExpected) {
|
|
78
|
+
const executions = polled.data || [];
|
|
79
|
+
const { dataplaneUrl, authConfig } = ctx;
|
|
80
|
+
const totalItems =
|
|
81
|
+
polled.meta && polled.meta.totalItems !== undefined
|
|
82
|
+
? polled.meta.totalItems
|
|
83
|
+
: executions.length;
|
|
84
|
+
const rows = [
|
|
85
|
+
checkRow1ExecutionsByDatasource({
|
|
86
|
+
executions,
|
|
87
|
+
totalItems,
|
|
88
|
+
minExpected
|
|
89
|
+
})
|
|
90
|
+
];
|
|
91
|
+
const rbacList = await queryRbacDecisions(dataplaneUrl, authConfig, {
|
|
92
|
+
targetDatasourceKey: ctx.datasourceKey,
|
|
93
|
+
pageSize: 100
|
|
94
|
+
});
|
|
95
|
+
rows.push(
|
|
96
|
+
checkRow2RbacDecisionsList({
|
|
97
|
+
rbacItems: rbacList.data,
|
|
98
|
+
totalItems: rbacList.meta && rbacList.meta.totalItems
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
const abacList = await queryAbacTraces(dataplaneUrl, authConfig, {
|
|
102
|
+
datasourceKey: ctx.datasourceKey,
|
|
103
|
+
pageSize: 100
|
|
104
|
+
});
|
|
105
|
+
rows.push(
|
|
106
|
+
checkRow3AbacTracesList({
|
|
107
|
+
abacItems: abacList.data,
|
|
108
|
+
totalItems: abacList.meta && abacList.meta.totalItems
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
return rows;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {Object} ctx
|
|
116
|
+
* @param {Array} executions
|
|
117
|
+
* @param {string[]} executionIds
|
|
118
|
+
* @returns {Promise<{ rows: import('../api/types/audit.types').AuditEvidenceMatrixRow[], execIds: string[] }>}
|
|
119
|
+
*/
|
|
120
|
+
async function buildPerExecutionAndTailRows(ctx, executions, executionIds) {
|
|
121
|
+
const { dataplaneUrl, authConfig } = ctx;
|
|
122
|
+
const execIds = resolveExecutionIdsForChecks(executions, executionIds);
|
|
123
|
+
const sampleId = execIds[0];
|
|
124
|
+
const base = { dataplaneUrl, authConfig, executionId: sampleId };
|
|
125
|
+
const rows = await Promise.all([
|
|
126
|
+
checkPerExecutionSub({ ...base, rowNum: 4, label: 'rbac', fetcher: getExecutionRbac, isValid: isRbacSubResourceValid }),
|
|
127
|
+
checkPerExecutionSub({ ...base, rowNum: 5, label: 'abac', fetcher: getExecutionAbac, isValid: isAbacSubResourceValid }),
|
|
128
|
+
checkPerExecutionSub({ ...base, rowNum: 6, label: 'errors', fetcher: getExecutionErrors, isValid: isErrorsValid }),
|
|
129
|
+
checkPerExecutionSub({ ...base, rowNum: 7, label: 'steps', fetcher: getExecutionSteps, isValid: isStepsValid })
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
let corrQueryRows = [];
|
|
133
|
+
if (ctx.correlationId) {
|
|
134
|
+
const corr = await queryExecutions(dataplaneUrl, authConfig, {
|
|
135
|
+
correlationId: ctx.correlationId,
|
|
136
|
+
pageSize: 100
|
|
137
|
+
});
|
|
138
|
+
corrQueryRows = corr.data || [];
|
|
139
|
+
}
|
|
140
|
+
let row8Pool = corrQueryRows.length ? corrQueryRows : executions;
|
|
141
|
+
const expected = ctx.expectedOperations || [];
|
|
142
|
+
if (expected.length && ctx.correlationId) {
|
|
143
|
+
const byOp = await fetchCorrelationExecutionsByOperation(
|
|
144
|
+
dataplaneUrl,
|
|
145
|
+
authConfig,
|
|
146
|
+
ctx.correlationId,
|
|
147
|
+
expected
|
|
148
|
+
);
|
|
149
|
+
if (byOp.length) row8Pool = byOp;
|
|
150
|
+
}
|
|
151
|
+
const row8Executions = resolveRow8Executions(ctx, executions, row8Pool);
|
|
152
|
+
rows.push(
|
|
153
|
+
checkRow8CorrelationGroup({
|
|
154
|
+
correlationId: ctx.correlationId,
|
|
155
|
+
executions: row8Executions,
|
|
156
|
+
expectedOperations: ctx.expectedOperations
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const traceIds = new Set();
|
|
161
|
+
for (const id of execIds.slice(0, Math.min(execIds.length, 12))) {
|
|
162
|
+
try {
|
|
163
|
+
const tr = await getExecutionTrace(dataplaneUrl, authConfig, id);
|
|
164
|
+
if (tr && tr.executionId) traceIds.add(String(tr.executionId));
|
|
165
|
+
else if (tr && tr.id) traceIds.add(String(tr.id));
|
|
166
|
+
} catch {
|
|
167
|
+
/* row 9 may fail */
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
rows.push(checkRow9Traces(traceIds, execIds));
|
|
171
|
+
return { rows, execIds };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
buildListMatrixRows,
|
|
176
|
+
buildPerExecutionAndTailRows
|
|
177
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Run Plan 407.1 nine-row audit evidence matrix against dataplane APIs
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { queryExecutions } = require('../api/audit.api');
|
|
10
|
+
const { pollExecutionsUntilReady } = require('./audit-evidence-poll');
|
|
11
|
+
const {
|
|
12
|
+
buildListMatrixRows,
|
|
13
|
+
buildPerExecutionAndTailRows
|
|
14
|
+
} = require('./audit-evidence-verify-matrix');
|
|
15
|
+
const { checkRow8CorrelationGroup, checkRow9Traces } = require('./audit-evidence-verify-helpers');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {Object} ctx
|
|
19
|
+
* @returns {number}
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* E2E runs tag executions with testRunId as correlationId; prefer that for list poll.
|
|
23
|
+
* @param {Object} ctx
|
|
24
|
+
* @returns {Object}
|
|
25
|
+
*/
|
|
26
|
+
function buildExecutionsPollQuery(ctx) {
|
|
27
|
+
const base = { pageSize: 100, sort: '-startedAt' };
|
|
28
|
+
if (ctx.correlationId) {
|
|
29
|
+
return { ...base, correlationId: ctx.correlationId };
|
|
30
|
+
}
|
|
31
|
+
return { ...base, datasourceKey: ctx.datasourceKey };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveMinExpected(ctx) {
|
|
35
|
+
const fromOps = ctx.expectedOperations && ctx.expectedOperations.length;
|
|
36
|
+
const fromIds = ctx.executionIds && ctx.executionIds.length;
|
|
37
|
+
if (fromIds && fromOps) {
|
|
38
|
+
return Math.max(1, Math.min(fromOps, fromIds));
|
|
39
|
+
}
|
|
40
|
+
return Math.max(1, fromOps || fromIds || 1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {string} datasourceKey
|
|
45
|
+
* @param {Object} ctx
|
|
46
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceVerification}
|
|
47
|
+
*/
|
|
48
|
+
function failedMissingIds(datasourceKey, ctx) {
|
|
49
|
+
return {
|
|
50
|
+
status: 'failed',
|
|
51
|
+
datasourceKey,
|
|
52
|
+
correlationId: ctx.correlationId || null,
|
|
53
|
+
executionIds: [],
|
|
54
|
+
matrix: [
|
|
55
|
+
{
|
|
56
|
+
row: 0,
|
|
57
|
+
status: 'failed',
|
|
58
|
+
detail: 'missing audit.executionIds in envelope',
|
|
59
|
+
code: 'auditIdsMissing'
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {Object} ctx
|
|
67
|
+
* @returns {Promise<import('../api/types/audit.types').AuditEvidenceVerification>}
|
|
68
|
+
*/
|
|
69
|
+
async function verifyAuditEvidenceMatrix(ctx) {
|
|
70
|
+
const datasourceKey = ctx.datasourceKey || '';
|
|
71
|
+
const executionIds = Array.isArray(ctx.executionIds) ? ctx.executionIds : [];
|
|
72
|
+
if (!executionIds.length && !ctx.correlationId) {
|
|
73
|
+
return failedMissingIds(datasourceKey, ctx);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { dataplaneUrl, authConfig } = ctx;
|
|
77
|
+
const minExpected = resolveMinExpected(ctx);
|
|
78
|
+
const execQuery = buildExecutionsPollQuery(ctx);
|
|
79
|
+
const polled = await pollExecutionsUntilReady({
|
|
80
|
+
fetchExecutions: () => queryExecutions(dataplaneUrl, authConfig, execQuery),
|
|
81
|
+
minExpected,
|
|
82
|
+
executionIds,
|
|
83
|
+
maxWaitMs: ctx.maxWaitMs,
|
|
84
|
+
intervalMs: ctx.intervalMs
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const executions = polled.data || [];
|
|
88
|
+
const matrix = await buildListMatrixRows(ctx, polled, minExpected);
|
|
89
|
+
const tail = await buildPerExecutionAndTailRows(ctx, executions, executionIds);
|
|
90
|
+
matrix.push(...tail.rows);
|
|
91
|
+
|
|
92
|
+
const passed = matrix.every(r => r.status === 'passed');
|
|
93
|
+
return {
|
|
94
|
+
status: passed ? 'passed' : 'failed',
|
|
95
|
+
datasourceKey,
|
|
96
|
+
correlationId: ctx.correlationId || null,
|
|
97
|
+
executionIds: tail.execIds,
|
|
98
|
+
matrix
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
verifyAuditEvidenceMatrix,
|
|
104
|
+
buildExecutionsPollQuery,
|
|
105
|
+
resolveMinExpected,
|
|
106
|
+
checkRow8CorrelationGroup,
|
|
107
|
+
checkRow9Traces
|
|
108
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Batch upload loop for datasource load (plan 144)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { bulkSyncRecords } = require('../api/records-bulk.api');
|
|
10
|
+
const { withTransientRetry, errorStatusCode } = require('./bulk-loader-service-retry');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {Object} state
|
|
14
|
+
* @param {number} index
|
|
15
|
+
* @param {Error} err
|
|
16
|
+
* @param {Object[]} batchRecords
|
|
17
|
+
*/
|
|
18
|
+
function recordBatchFailure(state, index, err, batchRecords) {
|
|
19
|
+
state.batches.push({
|
|
20
|
+
index: index + 1,
|
|
21
|
+
total: state.chunkCount,
|
|
22
|
+
error: err.message || String(err),
|
|
23
|
+
statusCode: errorStatusCode(err),
|
|
24
|
+
recordCount: batchRecords.length,
|
|
25
|
+
failed: batchRecords.map(r => ({ key: r.key, message: err.message || String(err) }))
|
|
26
|
+
});
|
|
27
|
+
state.totals.failedCount += batchRecords.length;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {Object} state
|
|
32
|
+
* @param {number} index
|
|
33
|
+
* @param {Object} response
|
|
34
|
+
* @param {Object[]} batchRecords
|
|
35
|
+
*/
|
|
36
|
+
function recordBatchSuccess(state, index, response, batchRecords) {
|
|
37
|
+
const inserted = response.insertedCount || 0;
|
|
38
|
+
const updated = response.updatedCount || 0;
|
|
39
|
+
const processed = response.totalProcessed || batchRecords.length;
|
|
40
|
+
const failedInBatch = Math.max(0, batchRecords.length - processed);
|
|
41
|
+
|
|
42
|
+
state.batches.push({
|
|
43
|
+
index: index + 1,
|
|
44
|
+
total: state.chunkCount,
|
|
45
|
+
insertedCount: inserted,
|
|
46
|
+
updatedCount: updated,
|
|
47
|
+
totalProcessed: processed,
|
|
48
|
+
failed: failedInBatch > 0 ? [{ message: 'Some records were not processed' }] : []
|
|
49
|
+
});
|
|
50
|
+
state.totals.insertedCount += inserted;
|
|
51
|
+
state.totals.updatedCount += updated;
|
|
52
|
+
state.totals.failedCount += failedInBatch;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @async
|
|
57
|
+
* @param {Object} params
|
|
58
|
+
* @returns {Promise<{ batches: Object[], totals: Object }>}
|
|
59
|
+
*/
|
|
60
|
+
async function uploadRecordBatches(params) {
|
|
61
|
+
const { chunks, syncType, dataplaneUrl, datasourceKey, authConfig } = params;
|
|
62
|
+
const state = {
|
|
63
|
+
batches: [],
|
|
64
|
+
totals: { insertedCount: 0, updatedCount: 0, failedCount: 0 },
|
|
65
|
+
chunkCount: chunks.length
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
69
|
+
const batchRecords = chunks[i];
|
|
70
|
+
const body = { syncType, sync: false, records: batchRecords };
|
|
71
|
+
try {
|
|
72
|
+
const response = await withTransientRetry(() =>
|
|
73
|
+
bulkSyncRecords(dataplaneUrl, datasourceKey, authConfig, body)
|
|
74
|
+
);
|
|
75
|
+
recordBatchSuccess(state, i, response, batchRecords);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
recordBatchFailure(state, i, err, batchRecords);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { batches: state.batches, totals: state.totals };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
uploadRecordBatches,
|
|
86
|
+
recordBatchFailure,
|
|
87
|
+
recordBatchSuccess
|
|
88
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Retry helpers for bulk loader (plan 144)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const MAX_RETRIES = 3;
|
|
10
|
+
const RETRY_STATUS = new Set([429, 500, 502, 503, 504]);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {Error} err
|
|
14
|
+
* @returns {number|null}
|
|
15
|
+
*/
|
|
16
|
+
function errorStatusCode(err) {
|
|
17
|
+
if (!err || typeof err !== 'object') return null;
|
|
18
|
+
if (typeof err.statusCode === 'number') return err.statusCode;
|
|
19
|
+
if (err.response && typeof err.response.status === 'number') return err.response.status;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sleep(ms) {
|
|
24
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @async
|
|
29
|
+
* @param {Function} fn
|
|
30
|
+
* @returns {Promise<*>}
|
|
31
|
+
*/
|
|
32
|
+
async function withTransientRetry(fn) {
|
|
33
|
+
let lastErr;
|
|
34
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt += 1) {
|
|
35
|
+
try {
|
|
36
|
+
return await fn();
|
|
37
|
+
} catch (err) {
|
|
38
|
+
lastErr = err;
|
|
39
|
+
const code = errorStatusCode(err);
|
|
40
|
+
if (!code || !RETRY_STATUS.has(code) || attempt >= MAX_RETRIES - 1) {
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
await sleep(500 * (attempt + 1));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
throw lastErr;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
withTransientRetry,
|
|
51
|
+
errorStatusCode
|
|
52
|
+
};
|