feat(foundry): support restricted subject aspects
This commit is contained in:
@@ -1,16 +1,20 @@
|
||||
const IDENTIFIER = /^[a-z][a-z0-9._:-]{1,159}$/;
|
||||
const FIELD = /^[a-z][a-z0-9_.-]{0,127}$/;
|
||||
const SEMVER = /^\d+\.\d+\.\d+$/;
|
||||
const SECRET_LIKE = /(?:token|secret|password|authorization|access[_-]?token|refresh[_-]?token|api[_-]?key|imei|phone|decrypt|address|raw[_-]?params?)/i;
|
||||
const SECRET_MATERIAL = /(?:token|secret|password|authorization|access[_-]?token|refresh[_-]?token|api[_-]?key|decrypt|raw[_-]?params?)/i;
|
||||
const RESTRICTED_IDENTIFIER = /(?:imei|phone|telephone|address)/i;
|
||||
const PROFILE_KEYS = new Set(["id", "version", "title", "semanticTypes", "defaultTabId", "tabs"]);
|
||||
const TAB_KEYS = new Set(["id", "label", "emptyMessage", "sections"]);
|
||||
const SECTION_KEYS = new Set(["id", "label", "fields"]);
|
||||
const FIELD_KEYS = new Set(["id", "aspectId", "source", "field", "label", "format", "unit", "allowedReadingIds"]);
|
||||
const FIELD_KEYS = new Set([
|
||||
"id", "aspectId", "source", "field", "label", "format", "unit", "allowedReadingIds", "dataClass",
|
||||
]);
|
||||
const SOURCES = new Set(["fact", "attribute", "geometry", "context"]);
|
||||
const FORMATS = new Set([
|
||||
"text", "number", "timestamp", "boolean", "coordinate",
|
||||
"signal_state", "movement_state", "telemetry_readings",
|
||||
]);
|
||||
const DATA_CLASSES = new Set(["operational", "restricted"]);
|
||||
const FACT_FIELDS = new Set(["sourceId", "semanticType", "observedAt", "receivedAt", "presentationStatus"]);
|
||||
const GEOMETRY_FIELDS = new Set(["latitude", "longitude"]);
|
||||
const CONTEXT_FIELDS = new Set(["dataProductId", "bindingId"]);
|
||||
@@ -75,11 +79,22 @@ function normalizeField(value) {
|
||||
if (!SOURCES.has(source)) fail("invalid_map_subject_detail_profile_field_source");
|
||||
if (!FORMATS.has(format)) fail("invalid_map_subject_detail_profile_field_format");
|
||||
const field = sourceField(source, value.field);
|
||||
if (source === "attribute" && SECRET_LIKE.test(field)) fail("map_subject_detail_profile_restricted_field");
|
||||
const dataClass = value.dataClass === undefined
|
||||
? "operational"
|
||||
: text(value.dataClass, 32, "invalid_map_subject_detail_profile_field_data_class");
|
||||
if (!DATA_CLASSES.has(dataClass)) fail("invalid_map_subject_detail_profile_field_data_class");
|
||||
if (source === "attribute" && SECRET_MATERIAL.test(field)) {
|
||||
fail("map_subject_detail_profile_secret_field");
|
||||
}
|
||||
if (source === "attribute" && RESTRICTED_IDENTIFIER.test(field) && dataClass !== "restricted") {
|
||||
fail("map_subject_detail_profile_restricted_field");
|
||||
}
|
||||
const allowedReadingIds = value.allowedReadingIds === undefined
|
||||
? []
|
||||
: fields(value.allowedReadingIds, 0, 256, "invalid_map_subject_detail_profile_reading_ids");
|
||||
if (allowedReadingIds.some((item) => SECRET_LIKE.test(item))) fail("map_subject_detail_profile_restricted_reading_id");
|
||||
if (allowedReadingIds.some((item) => SECRET_MATERIAL.test(item) || RESTRICTED_IDENTIFIER.test(item))) {
|
||||
fail("map_subject_detail_profile_restricted_reading_id");
|
||||
}
|
||||
if (format === "telemetry_readings" && (source !== "attribute" || field !== "sensor_readings")) {
|
||||
fail("map_subject_detail_profile_readings_source_invalid");
|
||||
}
|
||||
@@ -93,8 +108,9 @@ function normalizeField(value) {
|
||||
aspectId: identifier(value.aspectId ?? "primary", "invalid_map_subject_detail_profile_aspect_id"),
|
||||
source,
|
||||
field,
|
||||
label: safeLabel(value.label, 100, "invalid_map_subject_detail_profile_field_label"),
|
||||
label: fieldLabel(value.label, dataClass),
|
||||
format,
|
||||
dataClass,
|
||||
...(unit ? { unit } : {}),
|
||||
...(format === "telemetry_readings" ? { allowedReadingIds } : {}),
|
||||
};
|
||||
@@ -143,7 +159,15 @@ function fields(value, min, max, code) {
|
||||
|
||||
function safeLabel(value, max, code) {
|
||||
const normalized = text(value, max, code);
|
||||
if (SECRET_LIKE.test(normalized)) fail(code);
|
||||
if (SECRET_MATERIAL.test(normalized)) fail(code);
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function fieldLabel(value, dataClass) {
|
||||
const normalized = safeLabel(value, 100, "invalid_map_subject_detail_profile_field_label");
|
||||
if (RESTRICTED_IDENTIFIER.test(normalized) && dataClass !== "restricted") {
|
||||
fail("map_subject_detail_profile_restricted_field");
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user