@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,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Extract audit evidence inputs from DatasourceTestRun envelope or debug log
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fsp = require('node:fs').promises;
|
|
11
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
12
|
+
const { resolveAppKeyForDatasource } = require('./resolve-app');
|
|
13
|
+
const { getLatestLogPath } = require('./log-viewer');
|
|
14
|
+
const {
|
|
15
|
+
executionIdsFromDebugEnvelope,
|
|
16
|
+
cipOperationsFromDebugEnvelope,
|
|
17
|
+
dedupeIds: dedupeIdsFromDebug
|
|
18
|
+
} = require('./audit-evidence-extract-debug');
|
|
19
|
+
|
|
20
|
+
/** @param {Array<*>} ids @returns {string[]} */
|
|
21
|
+
function dedupeIds(ids) {
|
|
22
|
+
return dedupeIdsFromDebug(ids);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {Object|null|undefined} envelope
|
|
27
|
+
* @returns {string[]}
|
|
28
|
+
*/
|
|
29
|
+
function executionIdsFromEnvelope(envelope) {
|
|
30
|
+
const audit = envelope && envelope.audit;
|
|
31
|
+
const merged = [
|
|
32
|
+
...(audit && audit.executionIds ? audit.executionIds : []),
|
|
33
|
+
...executionIdsFromDebugEnvelope(envelope)
|
|
34
|
+
];
|
|
35
|
+
return dedupeIds(merged);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* E2E uses testRunId as CipExecution.correlationId (see external_data_e2e_helpers).
|
|
40
|
+
* @param {Object|null|undefined} envelope
|
|
41
|
+
* @returns {string|null}
|
|
42
|
+
*/
|
|
43
|
+
function correlationIdFromAsyncDebug(envelope) {
|
|
44
|
+
const dbg = envelope && envelope.debug;
|
|
45
|
+
const asyncDbg = dbg && dbg.e2eAsyncDebug;
|
|
46
|
+
if (!asyncDbg || typeof asyncDbg !== 'object') return null;
|
|
47
|
+
const requestId =
|
|
48
|
+
asyncDbg.requestId !== undefined && asyncDbg.requestId !== null
|
|
49
|
+
? String(asyncDbg.requestId).trim()
|
|
50
|
+
: '';
|
|
51
|
+
if (requestId) return requestId;
|
|
52
|
+
const nested =
|
|
53
|
+
asyncDbg.testRunId !== undefined && asyncDbg.testRunId !== null
|
|
54
|
+
? String(asyncDbg.testRunId).trim()
|
|
55
|
+
: '';
|
|
56
|
+
return nested || null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Ephemeral envelope runId (run-*) is not stored as CipExecution.correlationId.
|
|
61
|
+
* @param {string} runId
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
function isEphemeralEnvelopeRunId(runId) {
|
|
65
|
+
return /^run-[a-f0-9]{12}$/i.test(runId);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function correlationIdFromEnvelope(envelope) {
|
|
69
|
+
if (!envelope || typeof envelope !== 'object') return null;
|
|
70
|
+
const testRunId =
|
|
71
|
+
envelope.testRunId !== undefined && envelope.testRunId !== null
|
|
72
|
+
? String(envelope.testRunId).trim()
|
|
73
|
+
: '';
|
|
74
|
+
if (testRunId && !isEphemeralEnvelopeRunId(testRunId)) return testRunId;
|
|
75
|
+
const fromDebug = correlationIdFromAsyncDebug(envelope);
|
|
76
|
+
if (fromDebug) return fromDebug;
|
|
77
|
+
const runId =
|
|
78
|
+
envelope.runId !== undefined && envelope.runId !== null ? String(envelope.runId).trim() : '';
|
|
79
|
+
if (runId && !isEphemeralEnvelopeRunId(runId)) return runId;
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Expected capacity/sync operations for matrix rows 1 and 8.
|
|
85
|
+
* @param {Object|null|undefined} envelope
|
|
86
|
+
* @returns {string[]}
|
|
87
|
+
*/
|
|
88
|
+
function expectedOperationsFromEnvelope(envelope) {
|
|
89
|
+
const fromCapacity = cipOperationsFromDebugEnvelope(envelope);
|
|
90
|
+
if (fromCapacity.length) return fromCapacity;
|
|
91
|
+
const ops = new Set();
|
|
92
|
+
const caps = envelope && Array.isArray(envelope.capabilities) ? envelope.capabilities : [];
|
|
93
|
+
for (const cap of caps) {
|
|
94
|
+
if (!cap || cap.status === 'skipped' || !cap.key) continue;
|
|
95
|
+
const key = String(cap.key).trim();
|
|
96
|
+
if (['insert', 'update', 'get', 'delete', 'list', 'create'].includes(key)) {
|
|
97
|
+
ops.add(key === 'create' ? 'insert' : key);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const integration = envelope && envelope.integration;
|
|
101
|
+
if (integration && integration.sync && integration.sync.status === 'ok') {
|
|
102
|
+
ops.add('list');
|
|
103
|
+
}
|
|
104
|
+
return [...ops];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {Object|null|undefined} envelope
|
|
109
|
+
* @returns {{ datasourceKey: string, correlationId: string|null, executionIds: string[], expectedOperations: string[] }}
|
|
110
|
+
*/
|
|
111
|
+
function extractAuditEvidenceContext(envelope, opts = {}) {
|
|
112
|
+
if (!envelope || typeof envelope !== 'object') {
|
|
113
|
+
return {
|
|
114
|
+
datasourceKey: opts.datasourceKey ? String(opts.datasourceKey).trim() : '',
|
|
115
|
+
correlationId: null,
|
|
116
|
+
executionIds: [],
|
|
117
|
+
expectedOperations: []
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const datasourceKey =
|
|
121
|
+
opts.datasourceKey !== undefined && opts.datasourceKey !== null
|
|
122
|
+
? String(opts.datasourceKey).trim()
|
|
123
|
+
: envelope.datasourceKey !== undefined && envelope.datasourceKey !== null
|
|
124
|
+
? String(envelope.datasourceKey).trim()
|
|
125
|
+
: '';
|
|
126
|
+
return {
|
|
127
|
+
datasourceKey,
|
|
128
|
+
correlationId: correlationIdFromEnvelope(envelope),
|
|
129
|
+
executionIds: executionIdsFromEnvelope(envelope),
|
|
130
|
+
expectedOperations: expectedOperationsFromEnvelope(envelope)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {Object} logPayload - Saved test-e2e debug log `{ request, response }`
|
|
136
|
+
* @returns {Object|null}
|
|
137
|
+
*/
|
|
138
|
+
function envelopeFromLogPayload(logPayload) {
|
|
139
|
+
if (!logPayload || typeof logPayload !== 'object') return null;
|
|
140
|
+
if (logPayload.response && typeof logPayload.response === 'object') {
|
|
141
|
+
return logPayload.response;
|
|
142
|
+
}
|
|
143
|
+
return logPayload;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @async
|
|
148
|
+
* @param {string} datasourceKey
|
|
149
|
+
* @param {Object} options
|
|
150
|
+
* @param {string} [options.app]
|
|
151
|
+
* @param {string} [options.file]
|
|
152
|
+
* @returns {Promise<Object|null>}
|
|
153
|
+
*/
|
|
154
|
+
async function loadEnvelopeFromLatestE2eLog(datasourceKey, options = {}) {
|
|
155
|
+
if (options.file) {
|
|
156
|
+
const raw = await fsp.readFile(path.resolve(options.file), 'utf8');
|
|
157
|
+
const parsed = JSON.parse(raw);
|
|
158
|
+
return envelopeFromLogPayload(parsed);
|
|
159
|
+
}
|
|
160
|
+
const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
|
|
161
|
+
const logsDir = path.join(getIntegrationPath(appKey), 'logs');
|
|
162
|
+
const logPath = await getLatestLogPath(logsDir, 'test-e2e-');
|
|
163
|
+
if (!logPath) return null;
|
|
164
|
+
const raw = await fsp.readFile(logPath, 'utf8');
|
|
165
|
+
const parsed = JSON.parse(raw);
|
|
166
|
+
return envelopeFromLogPayload(parsed);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
dedupeIds,
|
|
171
|
+
executionIdsFromEnvelope,
|
|
172
|
+
correlationIdFromEnvelope,
|
|
173
|
+
expectedOperationsFromEnvelope,
|
|
174
|
+
extractAuditEvidenceContext,
|
|
175
|
+
envelopeFromLogPayload,
|
|
176
|
+
loadEnvelopeFromLatestE2eLog
|
|
177
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Plan 407.1 nine-row audit evidence matrix checks (pure helpers)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const CAPTURED = 'captured';
|
|
10
|
+
|
|
11
|
+
const RBAC_SKIP_REASON_PREFIXES = [
|
|
12
|
+
'rbacAuditInternalPath',
|
|
13
|
+
'rbacAuditApiKeyBypass',
|
|
14
|
+
'rbacAudit'
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {import('../api/types/audit.types').AuditEvidenceMatrixRow} row
|
|
19
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
20
|
+
*/
|
|
21
|
+
function passRow(row, detail, code) {
|
|
22
|
+
const out = { ...row, status: 'passed', detail };
|
|
23
|
+
if (code) {
|
|
24
|
+
out.code = code;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {import('../api/types/audit.types').AuditEvidenceMatrixRow} row
|
|
31
|
+
* @param {string} detail
|
|
32
|
+
* @param {string} [code]
|
|
33
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
34
|
+
*/
|
|
35
|
+
function failRow(row, detail, code) {
|
|
36
|
+
return { ...row, status: 'failed', detail, code: code || row.code };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {*} ref
|
|
41
|
+
* @returns {boolean}
|
|
42
|
+
*/
|
|
43
|
+
function hasFkRef(ref) {
|
|
44
|
+
if (!ref || typeof ref !== 'object') return false;
|
|
45
|
+
const id = ref.id !== undefined && ref.id !== null ? String(ref.id).trim() : '';
|
|
46
|
+
const key = ref.key !== undefined && ref.key !== null ? String(ref.key).trim() : '';
|
|
47
|
+
return Boolean(id || key);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {Object} item
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
function executionRowCaptured(item) {
|
|
55
|
+
if (!item || typeof item !== 'object') return false;
|
|
56
|
+
const rbacOk = item.rbacAuditStatus === CAPTURED && hasFkRef(item.rbacDecision);
|
|
57
|
+
const abacOk = item.abacAuditStatus === CAPTURED && hasFkRef(item.abacTrace);
|
|
58
|
+
return rbacOk && abacOk;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Row 1 — executions by datasource.
|
|
63
|
+
* @param {Object} input
|
|
64
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
65
|
+
*/
|
|
66
|
+
function checkRow1ExecutionsByDatasource(input) {
|
|
67
|
+
const row = { row: 1, status: 'failed', detail: '', code: 'executionsByDatasource' };
|
|
68
|
+
const { executions, minExpected } = input;
|
|
69
|
+
const total = input.totalItems !== undefined ? input.totalItems : executions.length;
|
|
70
|
+
if (total < minExpected) {
|
|
71
|
+
return failRow(row, `executions=${total} expected>=${minExpected}`, 'executionsCountLow');
|
|
72
|
+
}
|
|
73
|
+
const captured = executions.filter(executionRowCaptured).length;
|
|
74
|
+
if (captured < minExpected) {
|
|
75
|
+
return failRow(
|
|
76
|
+
row,
|
|
77
|
+
`executions=${total} captured=${captured} (need captured rbac/abac FKs)`,
|
|
78
|
+
'executionsNotCaptured'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return passRow(row, `executions=${total} captured=${captured}`, 'executionsByDatasource');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Row 2 — RBAC decisions list.
|
|
86
|
+
* @param {Object} input
|
|
87
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
88
|
+
*/
|
|
89
|
+
function checkRow2RbacDecisionsList(input) {
|
|
90
|
+
const row = { row: 2, status: 'failed', detail: '', code: 'rbacListEmpty' };
|
|
91
|
+
const items = input.rbacItems || [];
|
|
92
|
+
const total = input.totalItems !== undefined ? input.totalItems : items.length;
|
|
93
|
+
if (total < 1 || items.length < 1) {
|
|
94
|
+
return failRow(row, 'rbac-decisions list empty', 'rbacListEmpty');
|
|
95
|
+
}
|
|
96
|
+
const first = items[0];
|
|
97
|
+
const execRef = first && first.executionId;
|
|
98
|
+
if (!hasFkRef(execRef)) {
|
|
99
|
+
return failRow(row, 'rbac row missing executionId ref', 'rbacMissingExecutionId');
|
|
100
|
+
}
|
|
101
|
+
const reason = first && first.reason ? String(first.reason) : '';
|
|
102
|
+
const skipOk =
|
|
103
|
+
reason &&
|
|
104
|
+
RBAC_SKIP_REASON_PREFIXES.some(prefix => reason.startsWith(prefix) || reason.includes(prefix));
|
|
105
|
+
if (!skipOk && !reason) {
|
|
106
|
+
return failRow(row, 'rbac row missing skip/capture reason', 'rbacReasonMissing');
|
|
107
|
+
}
|
|
108
|
+
return passRow(row, `rbac rows=${total} reason=${reason || 'present'}`, 'rbacListOk');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Row 3 — ABAC traces list.
|
|
113
|
+
* @param {Object} input
|
|
114
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
115
|
+
*/
|
|
116
|
+
function checkRow3AbacTracesList(input) {
|
|
117
|
+
const row = { row: 3, status: 'failed', detail: '', code: 'abacListEmpty' };
|
|
118
|
+
const items = input.abacItems || [];
|
|
119
|
+
const total = input.totalItems !== undefined ? input.totalItems : items.length;
|
|
120
|
+
if (total < 1 || items.length < 1) {
|
|
121
|
+
return failRow(row, 'abac-traces list empty', 'abacListEmpty');
|
|
122
|
+
}
|
|
123
|
+
const first = items[0];
|
|
124
|
+
const decision = first && first.decision ? String(first.decision) : '';
|
|
125
|
+
const reason = first && first.reason ? String(first.reason) : '';
|
|
126
|
+
if (!reason) {
|
|
127
|
+
return failRow(row, 'abac row missing reason', 'abacReasonMissing');
|
|
128
|
+
}
|
|
129
|
+
return passRow(row, `abac rows=${total} decision=${decision || 'n/a'} reason=${reason}`, 'abacListOk');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Rows 4–7, 9 — per-execution sub-resources.
|
|
134
|
+
* @param {number} rowNum
|
|
135
|
+
* @param {string} code
|
|
136
|
+
* @param {string} label
|
|
137
|
+
* @param {*} payload
|
|
138
|
+
* @param {(payload: *) => boolean} isValid
|
|
139
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
|
|
140
|
+
*/
|
|
141
|
+
function checkPerExecutionRow(rowNum, code, label, payload, isValid) {
|
|
142
|
+
const row = { row: rowNum, status: 'failed', detail: '', code };
|
|
143
|
+
if (!isValid(payload)) {
|
|
144
|
+
return failRow(row, `${label} missing or invalid`, code);
|
|
145
|
+
}
|
|
146
|
+
return passRow(row, `${label} ok`, `${label}Ok`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
CAPTURED,
|
|
151
|
+
RBAC_SKIP_REASON_PREFIXES,
|
|
152
|
+
hasFkRef,
|
|
153
|
+
executionRowCaptured,
|
|
154
|
+
checkRow1ExecutionsByDatasource,
|
|
155
|
+
checkRow2RbacDecisionsList,
|
|
156
|
+
checkRow3AbacTracesList,
|
|
157
|
+
checkPerExecutionRow
|
|
158
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Normalize CIP execution operation labels for Plan 407 audit matrix
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Align list API operation values with builder E2E expectations (matches e2e_audit_operation.py).
|
|
11
|
+
* @param {*} operation
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
function normalizeAuditOperation(operation) {
|
|
15
|
+
const raw = operation !== undefined && operation !== null ? String(operation).trim() : '';
|
|
16
|
+
if (!raw) return '';
|
|
17
|
+
let op = raw === 'updateBasic' ? 'updatebasic' : raw.toLowerCase();
|
|
18
|
+
if (op === 'create') op = 'insert';
|
|
19
|
+
return op;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Array<{ operation?: string }>} executions
|
|
24
|
+
* @param {string[]} expectedOperations
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* API filter values to try for a matrix-expected operation (DB may use create vs insert).
|
|
29
|
+
* @param {string} expectedOp
|
|
30
|
+
* @returns {string[]}
|
|
31
|
+
*/
|
|
32
|
+
function operationQueryVariants(expectedOp) {
|
|
33
|
+
const n = normalizeAuditOperation(expectedOp);
|
|
34
|
+
if (!n) return [];
|
|
35
|
+
const variants = new Set([n]);
|
|
36
|
+
if (n === 'insert') variants.add('create');
|
|
37
|
+
if (n === 'updatebasic') {
|
|
38
|
+
variants.add('updateBasic');
|
|
39
|
+
variants.add('updatebasic');
|
|
40
|
+
}
|
|
41
|
+
if (n === 'updateaddress') {
|
|
42
|
+
variants.add('updateAddress');
|
|
43
|
+
variants.add('updateaddress');
|
|
44
|
+
}
|
|
45
|
+
return [...variants];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function executionsCoverExpectedOperations(executions, expectedOperations) {
|
|
49
|
+
const expected = (expectedOperations || [])
|
|
50
|
+
.map(normalizeAuditOperation)
|
|
51
|
+
.filter(Boolean);
|
|
52
|
+
if (!expected.length) return true;
|
|
53
|
+
const ops = new Set(
|
|
54
|
+
(executions || []).map(e => normalizeAuditOperation(e && e.operation)).filter(Boolean)
|
|
55
|
+
);
|
|
56
|
+
return expected.every(op => ops.has(op));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
normalizeAuditOperation,
|
|
61
|
+
operationQueryVariants,
|
|
62
|
+
executionsCoverExpectedOperations
|
|
63
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Bounded poll until audit execution rows are captured (407.3)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { executionRowCaptured } = require('./audit-evidence-matrix-rows');
|
|
10
|
+
|
|
11
|
+
const DEFAULT_MAX_WAIT_MS = 15000;
|
|
12
|
+
const DEFAULT_INTERVAL_MS = 2000;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {number} ms
|
|
16
|
+
* @returns {Promise<void>}
|
|
17
|
+
*/
|
|
18
|
+
function sleep(ms) {
|
|
19
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Array} executions
|
|
24
|
+
* @param {number} minExpected
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
function executionsReady(executions, minExpected) {
|
|
28
|
+
if (!Array.isArray(executions) || executions.length < minExpected) return false;
|
|
29
|
+
const captured = executions.filter(executionRowCaptured).length;
|
|
30
|
+
return captured >= minExpected;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Array} executions
|
|
35
|
+
* @param {string[]} executionIds
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
function allEnvelopeIdsPresent(executions, executionIds) {
|
|
39
|
+
if (!Array.isArray(executionIds) || !executionIds.length) return true;
|
|
40
|
+
const found = new Set(
|
|
41
|
+
(executions || []).map(e => (e && e.id ? String(e.id) : '')).filter(Boolean)
|
|
42
|
+
);
|
|
43
|
+
return executionIds.every(id => found.has(String(id)));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* E2E: wait until envelope executionIds are visible and captured in list API.
|
|
48
|
+
* @param {Array} executions
|
|
49
|
+
* @param {number} minExpected
|
|
50
|
+
* @param {string[]} [executionIds]
|
|
51
|
+
* @returns {boolean}
|
|
52
|
+
*/
|
|
53
|
+
function executionsReadyForVerify(executions, minExpected, executionIds) {
|
|
54
|
+
const min = Math.max(1, minExpected || 1);
|
|
55
|
+
if (Array.isArray(executionIds) && executionIds.length) {
|
|
56
|
+
const need = Math.min(min, executionIds.length);
|
|
57
|
+
if (!allEnvelopeIdsPresent(executions, executionIds)) return false;
|
|
58
|
+
const captured = executionIds.filter(id =>
|
|
59
|
+
(executions || []).some(
|
|
60
|
+
e => String(e.id) === String(id) && executionRowCaptured(e)
|
|
61
|
+
)
|
|
62
|
+
).length;
|
|
63
|
+
return captured >= need;
|
|
64
|
+
}
|
|
65
|
+
return executionsReady(executions, min);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @async
|
|
70
|
+
* @param {Object} opts
|
|
71
|
+
* @param {() => Promise<{ data: Array, meta: Object|null }>} opts.fetchExecutions
|
|
72
|
+
* @param {number} opts.minExpected
|
|
73
|
+
* @param {number} [opts.maxWaitMs]
|
|
74
|
+
* @param {number} [opts.intervalMs]
|
|
75
|
+
* @returns {Promise<{ data: Array, meta: Object|null, timedOut: boolean }>}
|
|
76
|
+
*/
|
|
77
|
+
async function pollExecutionsUntilReady(opts) {
|
|
78
|
+
const minExpected = Math.max(1, opts.minExpected || 1);
|
|
79
|
+
const executionIds = Array.isArray(opts.executionIds) ? opts.executionIds : [];
|
|
80
|
+
const maxWaitMs =
|
|
81
|
+
opts.maxWaitMs !== undefined && opts.maxWaitMs !== null ? opts.maxWaitMs : DEFAULT_MAX_WAIT_MS;
|
|
82
|
+
const intervalMs =
|
|
83
|
+
opts.intervalMs !== undefined && opts.intervalMs !== null
|
|
84
|
+
? opts.intervalMs
|
|
85
|
+
: DEFAULT_INTERVAL_MS;
|
|
86
|
+
const deadline = Date.now() + maxWaitMs;
|
|
87
|
+
let last = { data: [], meta: null };
|
|
88
|
+
|
|
89
|
+
while (Date.now() <= deadline) {
|
|
90
|
+
last = await opts.fetchExecutions();
|
|
91
|
+
if (executionsReadyForVerify(last.data, minExpected, executionIds)) {
|
|
92
|
+
return { ...last, timedOut: false };
|
|
93
|
+
}
|
|
94
|
+
if (Date.now() + intervalMs > deadline) break;
|
|
95
|
+
await sleep(intervalMs);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { ...last, timedOut: true };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
DEFAULT_MAX_WAIT_MS,
|
|
103
|
+
DEFAULT_INTERVAL_MS,
|
|
104
|
+
sleep,
|
|
105
|
+
executionsReady,
|
|
106
|
+
allEnvelopeIdsPresent,
|
|
107
|
+
executionsReadyForVerify,
|
|
108
|
+
pollExecutionsUntilReady
|
|
109
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Orchestrate audit evidence verification after E2E or from saved logs
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { resolveAppKeyForDatasource } = require('./resolve-app');
|
|
10
|
+
const { setupIntegrationTestAuth } = require('../external-system/test-auth');
|
|
11
|
+
const { getConfig } = require('../core/config');
|
|
12
|
+
const {
|
|
13
|
+
extractAuditEvidenceContext,
|
|
14
|
+
loadEnvelopeFromLatestE2eLog
|
|
15
|
+
} = require('./audit-evidence-extract');
|
|
16
|
+
const { verifyAuditEvidenceMatrix } = require('./audit-evidence-verify');
|
|
17
|
+
const {
|
|
18
|
+
displayAuditEvidenceMatrixTTY,
|
|
19
|
+
printAuditEvidenceVerificationJson
|
|
20
|
+
} = require('./audit-evidence-display');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} datasourceKey
|
|
24
|
+
* @param {Object} options
|
|
25
|
+
* @returns {Promise<{ authConfig: Object, dataplaneUrl: string }>}
|
|
26
|
+
*/
|
|
27
|
+
async function resolveAuditVerifyAuth(datasourceKey, options) {
|
|
28
|
+
if (options.authConfig && options.dataplaneUrl) {
|
|
29
|
+
return { authConfig: options.authConfig, dataplaneUrl: options.dataplaneUrl };
|
|
30
|
+
}
|
|
31
|
+
const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
|
|
32
|
+
const configObj = await getConfig();
|
|
33
|
+
const auth = await setupIntegrationTestAuth(appKey, options, configObj);
|
|
34
|
+
return { authConfig: auth.authConfig, dataplaneUrl: auth.dataplaneUrl };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {Object} envelope
|
|
39
|
+
* @param {string} datasourceKey
|
|
40
|
+
* @param {Object} [opts]
|
|
41
|
+
* @returns {Object}
|
|
42
|
+
*/
|
|
43
|
+
function buildVerifyContext(envelope, datasourceKey, opts = {}) {
|
|
44
|
+
const ctx = extractAuditEvidenceContext(envelope);
|
|
45
|
+
if (datasourceKey) ctx.datasourceKey = String(datasourceKey).trim();
|
|
46
|
+
if (opts.correlationId) ctx.correlationId = String(opts.correlationId).trim();
|
|
47
|
+
if (opts.executionId) {
|
|
48
|
+
const id = String(opts.executionId).trim();
|
|
49
|
+
if (id && !ctx.executionIds.includes(id)) ctx.executionIds.unshift(id);
|
|
50
|
+
}
|
|
51
|
+
return ctx;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @async
|
|
56
|
+
* @param {string} datasourceKey
|
|
57
|
+
* @param {Object} envelope - DatasourceTestRun
|
|
58
|
+
* @param {Object} options
|
|
59
|
+
* @returns {Promise<{ verification: import('../api/types/audit.types').AuditEvidenceVerification, exitCode: number }>}
|
|
60
|
+
*/
|
|
61
|
+
async function runAuditEvidenceVerification(datasourceKey, envelope, options = {}) {
|
|
62
|
+
const ctx = buildVerifyContext(envelope, datasourceKey, options);
|
|
63
|
+
if (!ctx.executionIds.length && !ctx.correlationId) {
|
|
64
|
+
const verification = failedVerification(
|
|
65
|
+
datasourceKey,
|
|
66
|
+
ctx,
|
|
67
|
+
'missing audit.executionIds and testRunId/correlationId',
|
|
68
|
+
'auditIdsMissing'
|
|
69
|
+
);
|
|
70
|
+
emitVerificationOutput(verification, options);
|
|
71
|
+
return { verification, exitCode: 1 };
|
|
72
|
+
}
|
|
73
|
+
let auth;
|
|
74
|
+
try {
|
|
75
|
+
auth = await resolveAuditVerifyAuth(datasourceKey, options);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
const verification = failedVerification(datasourceKey, ctx, err.message, 'authFailed');
|
|
78
|
+
emitVerificationOutput(verification, options);
|
|
79
|
+
return { verification, exitCode: 3 };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const verification = await verifyAuditEvidenceMatrix({
|
|
84
|
+
dataplaneUrl: auth.dataplaneUrl,
|
|
85
|
+
authConfig: auth.authConfig,
|
|
86
|
+
datasourceKey: ctx.datasourceKey,
|
|
87
|
+
correlationId: ctx.correlationId,
|
|
88
|
+
executionIds: ctx.executionIds,
|
|
89
|
+
expectedOperations: ctx.expectedOperations,
|
|
90
|
+
maxWaitMs: options.auditPollMaxWaitMs,
|
|
91
|
+
intervalMs: options.auditPollIntervalMs
|
|
92
|
+
});
|
|
93
|
+
emitVerificationOutput(verification, options);
|
|
94
|
+
return { verification, exitCode: verification.status === 'passed' ? 0 : 1 };
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const verification = failedVerification(datasourceKey, ctx, err.message, 'verifyError');
|
|
97
|
+
emitVerificationOutput(verification, options);
|
|
98
|
+
const code = err.status === 401 || err.status === 403 ? 3 : 1;
|
|
99
|
+
return { verification, exitCode: code };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {string} datasourceKey
|
|
105
|
+
* @param {Object} ctx
|
|
106
|
+
* @param {string} message
|
|
107
|
+
* @param {string} code
|
|
108
|
+
* @returns {import('../api/types/audit.types').AuditEvidenceVerification}
|
|
109
|
+
*/
|
|
110
|
+
function failedVerification(datasourceKey, ctx, message, code) {
|
|
111
|
+
return {
|
|
112
|
+
status: 'failed',
|
|
113
|
+
datasourceKey,
|
|
114
|
+
correlationId: ctx.correlationId || null,
|
|
115
|
+
executionIds: ctx.executionIds || [],
|
|
116
|
+
matrix: [{ row: 0, status: 'failed', detail: message, code }]
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {import('../api/types/audit.types').AuditEvidenceVerification} verification
|
|
122
|
+
* @param {Object} options
|
|
123
|
+
*/
|
|
124
|
+
function emitVerificationOutput(verification, options) {
|
|
125
|
+
if (options.json) {
|
|
126
|
+
printAuditEvidenceVerificationJson(verification);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!options.quiet) {
|
|
130
|
+
displayAuditEvidenceMatrixTTY(verification, options.verbose === true);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Run audit matrix when E2E succeeded and --verify-audit is set.
|
|
136
|
+
* @param {number} e2eExitCode
|
|
137
|
+
* @param {string} datasourceKey
|
|
138
|
+
* @param {Object|null} envelope
|
|
139
|
+
* @param {Object} options
|
|
140
|
+
* @returns {Promise<number>}
|
|
141
|
+
*/
|
|
142
|
+
async function maybeVerifyAuditAfterE2E(e2eExitCode, datasourceKey, envelope, options = {}) {
|
|
143
|
+
const enabled =
|
|
144
|
+
options.verifyAudit === true && options.noVerifyAudit !== true;
|
|
145
|
+
if (!enabled || e2eExitCode !== 0 || !envelope) {
|
|
146
|
+
return e2eExitCode;
|
|
147
|
+
}
|
|
148
|
+
const pollMsRaw = parseInt(String(options.auditPollMs || options.auditPollMaxWaitMs || '15000'), 10);
|
|
149
|
+
const pollInterval = parseInt(
|
|
150
|
+
String(options.auditPollIntervalMs || '2000'),
|
|
151
|
+
10
|
|
152
|
+
);
|
|
153
|
+
const ctx = buildVerifyContext(envelope, datasourceKey, options);
|
|
154
|
+
const pollMs = Math.max(
|
|
155
|
+
Number.isFinite(pollMsRaw) ? pollMsRaw : 15000,
|
|
156
|
+
(ctx.executionIds && ctx.executionIds.length >= 7) ? 30000 : 0
|
|
157
|
+
);
|
|
158
|
+
const { exitCode } = await runAuditEvidenceVerification(datasourceKey, envelope, {
|
|
159
|
+
app: options.app,
|
|
160
|
+
env: options.env,
|
|
161
|
+
verbose: options.verbose,
|
|
162
|
+
json: options.json,
|
|
163
|
+
authConfig: options.authConfig,
|
|
164
|
+
dataplaneUrl: options.dataplaneUrl,
|
|
165
|
+
auditPollMaxWaitMs: pollMs,
|
|
166
|
+
auditPollIntervalMs: Number.isFinite(pollInterval) ? pollInterval : 2000
|
|
167
|
+
});
|
|
168
|
+
return exitCode;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = {
|
|
172
|
+
runAuditEvidenceVerification,
|
|
173
|
+
maybeVerifyAuditAfterE2E,
|
|
174
|
+
loadEnvelopeFromLatestE2eLog,
|
|
175
|
+
buildVerifyContext,
|
|
176
|
+
resolveAuditVerifyAuth
|
|
177
|
+
};
|