OAAUTO1 执行自动化收口证据
这个提交包含在:
@@ -49,6 +49,18 @@ const ROLE_TARGETS = [
|
||||
{ id: "notification-deliveries", path: "/api/notifications/deliveries?limit=5", statuses: [403] },
|
||||
{ id: "system-feature-flags", path: "/api/system/feature-flags", statuses: [403] },
|
||||
{ id: "erp-status", path: "/api/integrations/erp/master-data/status", statuses: [403] },
|
||||
{ id: "payroll-items", path: "/api/payroll/items", statuses: [403] },
|
||||
{ id: "performance-cycles", path: "/api/performance/cycles", statuses: [403, 409] },
|
||||
{ id: "travel-requests", path: "/api/travel-requests?page=1&pageSize=5", statuses: [403, 409] },
|
||||
{
|
||||
id: "global-search-sensitive",
|
||||
path: "/api/search?q=TEST&limit=10",
|
||||
statuses: [200, 409],
|
||||
searchExpectations: {
|
||||
forbiddenEntityTypes: ["contract_registry", "confidential_doc_ref", "travel_request"],
|
||||
forbiddenHrefPrefixes: ["/contracts-registry", "/travel-requests"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -86,6 +98,18 @@ const ROLE_TARGETS = [
|
||||
{ id: "payment-batches", path: "/api/payment-batches?pageSize=5", statuses: [403, 409] },
|
||||
{ id: "audit-logs", path: "/api/audit-logs?pageSize=5", statuses: [403] },
|
||||
{ id: "erp-status", path: "/api/integrations/erp/master-data/status", statuses: [403] },
|
||||
{ id: "payroll-items", path: "/api/payroll/items", statuses: [403] },
|
||||
{ id: "performance-cycles", path: "/api/performance/cycles", statuses: [403, 409] },
|
||||
{ id: "travel-requests", path: "/api/travel-requests?page=1&pageSize=5", statuses: [403, 409] },
|
||||
{
|
||||
id: "global-search-sensitive",
|
||||
path: "/api/search?q=TEST&limit=10",
|
||||
statuses: [200, 409],
|
||||
searchExpectations: {
|
||||
forbiddenEntityTypes: ["contract_registry", "confidential_doc_ref", "travel_request"],
|
||||
forbiddenHrefPrefixes: ["/contracts-registry", "/travel-requests"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -124,6 +148,18 @@ const ROLE_TARGETS = [
|
||||
{ id: "notification-deliveries", path: "/api/notifications/deliveries?limit=5", statuses: [403] },
|
||||
{ id: "system-feature-flags", path: "/api/system/feature-flags", statuses: [403] },
|
||||
{ id: "erp-status", path: "/api/integrations/erp/master-data/status", statuses: [403] },
|
||||
{ id: "payroll-items", path: "/api/payroll/items", statuses: [403] },
|
||||
{ id: "performance-cycles", path: "/api/performance/cycles", statuses: [403, 409] },
|
||||
{ id: "travel-requests", path: "/api/travel-requests?page=1&pageSize=5", statuses: [403, 409] },
|
||||
{
|
||||
id: "global-search-sensitive",
|
||||
path: "/api/search?q=TEST&limit=10",
|
||||
statuses: [200, 409],
|
||||
searchExpectations: {
|
||||
forbiddenEntityTypes: ["contract_registry", "confidential_doc_ref", "travel_request"],
|
||||
forbiddenHrefPrefixes: ["/contracts-registry", "/travel-requests"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -197,6 +233,17 @@ const ROLE_TARGETS = [
|
||||
{ id: "payment-batches", path: "/api/payment-batches?pageSize=5", statuses: [403, 409] },
|
||||
{ id: "audit-logs", path: "/api/audit-logs?pageSize=5", statuses: [403] },
|
||||
{ id: "system-feature-flags", path: "/api/system/feature-flags", statuses: [403] },
|
||||
{ id: "payroll-items", path: "/api/payroll/items", statuses: [403] },
|
||||
{ id: "performance-cycles", path: "/api/performance/cycles", statuses: [403, 409] },
|
||||
{
|
||||
id: "global-search-sensitive",
|
||||
path: "/api/search?q=TEST&limit=10",
|
||||
statuses: [200, 409],
|
||||
searchExpectations: {
|
||||
forbiddenEntityTypes: ["contract_registry", "confidential_doc_ref", "travel_request"],
|
||||
forbiddenHrefPrefixes: ["/contracts-registry", "/travel-requests"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -231,6 +278,17 @@ const ROLE_TARGETS = [
|
||||
negative: [
|
||||
{ id: "system-feature-flags", path: "/api/system/feature-flags", statuses: [403] },
|
||||
{ id: "notification-deliveries", path: "/api/notifications/deliveries?limit=5", statuses: [403] },
|
||||
{ id: "payroll-items", path: "/api/payroll/items", statuses: [403] },
|
||||
{ id: "performance-cycles", path: "/api/performance/cycles", statuses: [403, 409] },
|
||||
{
|
||||
id: "global-search-sensitive",
|
||||
path: "/api/search?q=TEST&limit=10",
|
||||
statuses: [200, 409],
|
||||
searchExpectations: {
|
||||
forbiddenEntityTypes: ["contract_registry", "confidential_doc_ref", "travel_request"],
|
||||
forbiddenHrefPrefixes: ["/contracts-registry", "/travel-requests"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -255,6 +313,9 @@ const ROLE_TARGETS = [
|
||||
{ id: "audit-logs", path: "/api/audit-logs?pageSize=5", statuses: [200] },
|
||||
{ id: "notification-deliveries", path: "/api/notifications/deliveries?limit=5", statuses: [200] },
|
||||
{ id: "system-feature-flags", path: "/api/system/feature-flags", statuses: [200] },
|
||||
{ id: "payroll-items", path: "/api/payroll/items", statuses: [200] },
|
||||
{ id: "performance-cycles", path: "/api/performance/cycles", statuses: [200, 409] },
|
||||
{ id: "global-search", path: "/api/search?q=TEST&limit=10", statuses: [200, 409] },
|
||||
{ id: "assets", path: "/api/assets?pageSize=5", statuses: [200] },
|
||||
{
|
||||
id: "navigation",
|
||||
@@ -384,6 +445,33 @@ function evaluateNavigationExpectations(json, expectations) {
|
||||
return [...visible, ...hidden];
|
||||
}
|
||||
|
||||
function evaluateSearchExpectations(json, expectations) {
|
||||
if (!expectations || !Array.isArray(json?.data?.items)) return [];
|
||||
const items = json.data.items.filter((item) => item && typeof item === "object");
|
||||
const forbiddenEntityTypes = new Set(expectations.forbiddenEntityTypes || []);
|
||||
const forbiddenHrefPrefixes = expectations.forbiddenHrefPrefixes || [];
|
||||
const results = [];
|
||||
for (const entityType of forbiddenEntityTypes) {
|
||||
const matchedCount = items.filter((item) => item.entityType === entityType).length;
|
||||
results.push({
|
||||
type: "forbidden_entity_type",
|
||||
value: entityType,
|
||||
status: matchedCount === 0 ? "PASS" : "FAIL",
|
||||
matchedCount,
|
||||
});
|
||||
}
|
||||
for (const hrefPrefix of forbiddenHrefPrefixes) {
|
||||
const matchedCount = items.filter((item) => typeof item.href === "string" && item.href.startsWith(hrefPrefix)).length;
|
||||
results.push({
|
||||
type: "forbidden_href_prefix",
|
||||
value: hrefPrefix,
|
||||
status: matchedCount === 0 ? "PASS" : "FAIL",
|
||||
matchedCount,
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function collectSensitiveKeyHits(value, currentPath = "data", hits = []) {
|
||||
if (hits.length >= 12) return hits;
|
||||
if (Array.isArray(value)) {
|
||||
@@ -471,17 +559,20 @@ async function probe(accessToken, probe, kind) {
|
||||
const expected = probe.statuses.includes(response.status);
|
||||
const dataExpectations = response.ok ? evaluateDataExpectations(response.json, probe.dataExpectations) : [];
|
||||
const navigationExpectations = response.ok ? evaluateNavigationExpectations(response.json, probe.navigationExpectations) : [];
|
||||
const searchExpectations = response.ok ? evaluateSearchExpectations(response.json, probe.searchExpectations) : [];
|
||||
const dataExpectationFailed = dataExpectations.some((item) => item.status === "FAIL");
|
||||
const navigationExpectationFailed = navigationExpectations.some((item) => item.status === "FAIL");
|
||||
const searchExpectationFailed = searchExpectations.some((item) => item.status === "FAIL");
|
||||
const sensitiveKeyHits = response.ok ? collectSensitiveKeyHits(response.json?.data).slice(0, 12) : [];
|
||||
return {
|
||||
id: probe.id,
|
||||
kind,
|
||||
status: expected && !dataExpectationFailed && !navigationExpectationFailed && sensitiveKeyHits.length === 0 ? "PASS" : "FAIL",
|
||||
status: expected && !dataExpectationFailed && !navigationExpectationFailed && !searchExpectationFailed && sensitiveKeyHits.length === 0 ? "PASS" : "FAIL",
|
||||
httpStatus: response.status,
|
||||
expectedStatuses: probe.statuses,
|
||||
dataExpectations,
|
||||
navigationExpectations,
|
||||
searchExpectations,
|
||||
sensitiveKeyHits,
|
||||
summary: response.ok ? summarizeData(response.json) : {
|
||||
code: response.json?.error?.code ?? null,
|
||||
@@ -544,6 +635,7 @@ function collectIssues(results) {
|
||||
code: probeResult.summary?.code ?? null,
|
||||
failedDataExpectations: probeResult.dataExpectations?.filter((item) => item.status === "FAIL") || [],
|
||||
failedNavigationExpectations: probeResult.navigationExpectations?.filter((item) => item.status === "FAIL") || [],
|
||||
failedSearchExpectations: probeResult.searchExpectations?.filter((item) => item.status === "FAIL") || [],
|
||||
sensitiveKeyHits: probeResult.sensitiveKeyHits || [],
|
||||
});
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户