@7365admin1/core 3.48.1-staging.180 → 3.48.1-staging.182
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/.changeset/hid-configuration-write-allowlist.md +22 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +54 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +54 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/hid-hardening.test.mjs +92 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Constrain what a HID configuration write may contain, and stop storing password salts
|
|
6
|
+
|
|
7
|
+
`PUT /readers/:readerId/configuration` is a passthrough to the reader's
|
|
8
|
+
`set_configuration.fcgi`, and its body schema was `Joi.object().unknown(true)`.
|
|
9
|
+
That reaches `monitor.hostname`, `monitor.port` and `monitor.path` — the block
|
|
10
|
+
that decides which server receives every identification at that door — so one
|
|
11
|
+
request could redirect a reader's entire event stream. Writes are now limited to
|
|
12
|
+
the two modules the product actually writes, `pjsip` and `identifier`, which are
|
|
13
|
+
the only two `setReaderConfiguration` call sites in `layer-common`. Module
|
|
14
|
+
contents stay unvalidated (the vendor owns those keys), reading configuration is
|
|
15
|
+
unchanged, and the monitor block is still managed by the server through
|
|
16
|
+
`setReaderMonitor` and `configureReaderIntegration`, which do not use this
|
|
17
|
+
schema.
|
|
18
|
+
|
|
19
|
+
`sanitizeHidPayload` — the central redaction every `addEvent` already passes
|
|
20
|
+
through — did not include `salt`, so a password salt was still being persisted
|
|
21
|
+
beside the hash it belongs to. `salt`, `salt_value` and `saltvalue` are now
|
|
22
|
+
redacted like the rest. This does not touch records already stored.
|
package/dist/index.d.ts
CHANGED
|
@@ -10450,6 +10450,7 @@ declare function useBuildingLevelRepo(): {
|
|
|
10450
10450
|
getById: (_id: string | ObjectId, session?: ClientSession) => Promise<TBuildingLevel>;
|
|
10451
10451
|
updateLevelById: (_id: string | ObjectId, value: TBuildingLevel, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
10452
10452
|
deleteById: (_id: string | ObjectId, session?: ClientSession) => Promise<string>;
|
|
10453
|
+
deactivateByBuilding: (buildingId: string | ObjectId, levelIds?: (string | ObjectId)[], session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
10453
10454
|
bulkWriteLevels: (levels: TBuildingLevel[], session?: ClientSession) => Promise<{
|
|
10454
10455
|
insertedIds: {
|
|
10455
10456
|
[key: number]: any;
|
|
@@ -10659,6 +10660,21 @@ declare const schemaUpdateHidSitePermissions: Joi.ObjectSchema<any>;
|
|
|
10659
10660
|
declare const schemaHidAmicoExecuteActions: Joi.ObjectSchema<any>;
|
|
10660
10661
|
declare const schemaHidAmicoIntercomCall: Joi.ObjectSchema<any>;
|
|
10661
10662
|
declare const schemaHidAmicoConfiguration: Joi.ObjectSchema<any>;
|
|
10663
|
+
/**
|
|
10664
|
+
* A configuration WRITE is a passthrough to `set_configuration.fcgi`, so an
|
|
10665
|
+
* unrestricted body reaches `monitor.hostname`, `monitor.port` and
|
|
10666
|
+
* `monitor.path` - the block that decides which server receives every
|
|
10667
|
+
* identification at that door. A single request could redirect a reader's
|
|
10668
|
+
* entire event stream.
|
|
10669
|
+
*
|
|
10670
|
+
* Writes are limited to the two modules the product actually writes, which are
|
|
10671
|
+
* the only two `setReaderConfiguration` call sites in `layer-common`:
|
|
10672
|
+
* `pjsip` (HidIntercomManagement.vue) and `identifier`
|
|
10673
|
+
* (HidQrCodeConfiguration.vue). Module CONTENTS stay unvalidated - the vendor
|
|
10674
|
+
* owns those keys - and READING configuration is unchanged. The monitor block
|
|
10675
|
+
* is still managed by the server through `setReaderMonitor` and
|
|
10676
|
+
* `configureReaderIntegration`, which do not go through this schema.
|
|
10677
|
+
*/
|
|
10662
10678
|
declare const schemaHidAmicoSetConfiguration: Joi.ObjectSchema<any>;
|
|
10663
10679
|
declare const schemaHidAmicoMonitor: Joi.ObjectSchema<any>;
|
|
10664
10680
|
declare const schemaHidAmicoObjectOperation: Joi.ObjectSchema<any>;
|
package/dist/index.js
CHANGED
|
@@ -36906,6 +36906,34 @@ function useBuildingLevelRepo() {
|
|
|
36906
36906
|
}
|
|
36907
36907
|
}
|
|
36908
36908
|
}
|
|
36909
|
+
async function deactivateByBuilding(buildingId, levelIds = [], session) {
|
|
36910
|
+
let bId;
|
|
36911
|
+
try {
|
|
36912
|
+
bId = new import_mongodb70.ObjectId(buildingId);
|
|
36913
|
+
} catch {
|
|
36914
|
+
throw new import_node_server_utils107.BadRequestError("Invalid building ID.");
|
|
36915
|
+
}
|
|
36916
|
+
const or = [
|
|
36917
|
+
{ blockId: bId },
|
|
36918
|
+
{ blockId: String(bId) }
|
|
36919
|
+
];
|
|
36920
|
+
const refIds = (levelIds || []).map((id) => {
|
|
36921
|
+
try {
|
|
36922
|
+
return new import_mongodb70.ObjectId(id);
|
|
36923
|
+
} catch {
|
|
36924
|
+
return null;
|
|
36925
|
+
}
|
|
36926
|
+
}).filter((x) => x !== null);
|
|
36927
|
+
if (refIds.length)
|
|
36928
|
+
or.push({ _id: { $in: refIds } });
|
|
36929
|
+
const res = await collection.updateMany(
|
|
36930
|
+
{ status: "active" /* ACTIVE */, $or: or },
|
|
36931
|
+
{ $set: { status: "deleted" /* DELETED */, deletedAt: /* @__PURE__ */ new Date() } },
|
|
36932
|
+
{ session }
|
|
36933
|
+
);
|
|
36934
|
+
delCachedData();
|
|
36935
|
+
return res;
|
|
36936
|
+
}
|
|
36909
36937
|
async function bulkWriteLevels(levels, session) {
|
|
36910
36938
|
try {
|
|
36911
36939
|
const operations = levels.map((level) => {
|
|
@@ -37031,6 +37059,7 @@ function useBuildingLevelRepo() {
|
|
|
37031
37059
|
getById,
|
|
37032
37060
|
updateLevelById,
|
|
37033
37061
|
deleteById,
|
|
37062
|
+
deactivateByBuilding,
|
|
37034
37063
|
bulkWriteLevels,
|
|
37035
37064
|
batchUpdateByIds,
|
|
37036
37065
|
getBuildingLevelList,
|
|
@@ -37057,7 +37086,8 @@ function useBuildingService() {
|
|
|
37057
37086
|
const { deleteFile } = useFileService();
|
|
37058
37087
|
const {
|
|
37059
37088
|
bulkWriteLevels: _bulkWriteLevels,
|
|
37060
|
-
getActiveLevelsByIds: _getActiveLevelsByIds
|
|
37089
|
+
getActiveLevelsByIds: _getActiveLevelsByIds,
|
|
37090
|
+
deactivateByBuilding: _deactivateLevelsByBuilding
|
|
37061
37091
|
} = useBuildingLevelRepo();
|
|
37062
37092
|
function normalizeImportKey(value) {
|
|
37063
37093
|
return String(value ?? "").trim().replace(/\s+/g, " ").toLowerCase();
|
|
@@ -37212,17 +37242,29 @@ function useBuildingService() {
|
|
|
37212
37242
|
}
|
|
37213
37243
|
}
|
|
37214
37244
|
async function deleteById(id) {
|
|
37215
|
-
const
|
|
37216
|
-
if (
|
|
37245
|
+
const existingUnit = await getByBuilding(id);
|
|
37246
|
+
if (existingUnit) {
|
|
37217
37247
|
throw new import_node_server_utils108.BadRequestError(
|
|
37218
|
-
"Cannot delete building with existing
|
|
37248
|
+
"Cannot delete building with existing unit(s). Please delete the unit(s) first."
|
|
37219
37249
|
);
|
|
37220
37250
|
}
|
|
37251
|
+
const session = import_node_server_utils108.useAtlas.getClient()?.startSession();
|
|
37252
|
+
if (!session) {
|
|
37253
|
+
throw new import_node_server_utils108.BadRequestError("Database session not available.");
|
|
37254
|
+
}
|
|
37221
37255
|
try {
|
|
37222
|
-
|
|
37256
|
+
session.startTransaction();
|
|
37257
|
+
const building = await _getById(id);
|
|
37258
|
+
const levelIds = building?.levels ?? [];
|
|
37259
|
+
await _deleteById(id, session);
|
|
37260
|
+
await _deactivateLevelsByBuilding(id, levelIds, session);
|
|
37261
|
+
await session.commitTransaction();
|
|
37223
37262
|
return "Building deleted successfully.";
|
|
37224
37263
|
} catch (error) {
|
|
37264
|
+
await session.abortTransaction();
|
|
37225
37265
|
throw error;
|
|
37266
|
+
} finally {
|
|
37267
|
+
session.endSession();
|
|
37226
37268
|
}
|
|
37227
37269
|
}
|
|
37228
37270
|
async function uploadSpreadsheetBuilding(data, site) {
|
|
@@ -43463,7 +43505,10 @@ var schemaHidAmicoIntercomCall = import_joi64.default.object({
|
|
|
43463
43505
|
target: import_joi64.default.string().trim().min(1).max(255).pattern(/^(?:sip:)?[A-Za-z0-9_.+-]+(?:@[A-Za-z0-9.-]+(?::\d{1,5})?)?$/).required()
|
|
43464
43506
|
}).unknown(true);
|
|
43465
43507
|
var schemaHidAmicoConfiguration = import_joi64.default.object().pattern(import_joi64.default.string(), import_joi64.default.array().items(import_joi64.default.string())).min(1).unknown(true);
|
|
43466
|
-
var schemaHidAmicoSetConfiguration = import_joi64.default.object(
|
|
43508
|
+
var schemaHidAmicoSetConfiguration = import_joi64.default.object({
|
|
43509
|
+
pjsip: import_joi64.default.object().unknown(true),
|
|
43510
|
+
identifier: import_joi64.default.object().unknown(true)
|
|
43511
|
+
}).min(1).unknown(false);
|
|
43467
43512
|
var schemaHidAmicoMonitor = import_joi64.default.object({
|
|
43468
43513
|
enabled: import_joi64.default.boolean().required()
|
|
43469
43514
|
});
|
|
@@ -43672,6 +43717,9 @@ var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
43672
43717
|
"pin",
|
|
43673
43718
|
"pin_value",
|
|
43674
43719
|
"pinvalue",
|
|
43720
|
+
"salt",
|
|
43721
|
+
"salt_value",
|
|
43722
|
+
"saltvalue",
|
|
43675
43723
|
"card_value",
|
|
43676
43724
|
"cardvalue",
|
|
43677
43725
|
"cardno",
|