@7365admin1/core 3.48.1-staging.202 → 3.48.1-staging.203

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.
@@ -0,0 +1,35 @@
1
+ ---
2
+ "@7365admin1/core": patch
3
+ ---
4
+
5
+ Decide the HRM Labs location server-side, so an entitled caller cannot read
6
+ another site's roster.
7
+
8
+ `requireSiteReach(req, siteId, ATTENDANCE_READ)` guards all four manpower-attendance
9
+ reads, but `siteId` is not what selects the roster — it only picks the local
10
+ `manpower-settings` row. The roster is selected by `locationName`, the field HRM Labs
11
+ is asked for, and that came straight from the request body's `siteName` with nothing
12
+ checking it against `siteId`.
13
+
14
+ So the two could disagree. A caller entitled to site A could send site A's `siteId`,
15
+ pass the guard, and pair it with **another** site's `siteName` — and read that site's
16
+ employee roster back: names, staff numbers, job titles, NRICs
17
+ (`identificationNumber`) and the check-in/check-out photographs. The exposure had been
18
+ narrowed from anyone on the internet to any signed-in account; this closes it.
19
+
20
+ The location now comes from `config.siteName`, the settings row the caller was already
21
+ entitled to, via one `locationOf()` helper called in each of the four functions. That
22
+ is the same source the screen reads from: the security app fills `siteId` and
23
+ `siteName` from one HRM Labs site-list entry (`site.id` / `site.text`),
24
+ `manpowerMonitoringSchema` makes `siteName` required on both write paths, and
25
+ `TManpowerMonitoringUpdate` cannot change it — so for every legitimate row the pair
26
+ already agreed and the server simply stops asking.
27
+
28
+ A row with no stored name is refused rather than falling back to the body; a fallback
29
+ would re-open the hole for exactly the rows an attacker would want to name for
30
+ themselves.
31
+
32
+ Nothing legitimate changes. The Joi schemas still accept `siteName`, so no client needs
33
+ a coordinated release — it is accepted and ignored. The three internal callers
34
+ (`manpower.event.ts` and the two `cron.util.ts` sweeps) get more reliable, not less:
35
+ they were passing `manpower-remarks.siteName`, which that model marks optional.
package/dist/index.js CHANGED
@@ -78089,6 +78089,15 @@ var {
78089
78089
  totalCountPerStatus: totalCountPerStatus2,
78090
78090
  chartCountData: chartCountData2
78091
78091
  } = hrmlabs_attendance_util_default;
78092
+ function locationOf(config2) {
78093
+ const name = typeof config2?.siteName === "string" ? config2.siteName.trim() : "";
78094
+ if (!name) {
78095
+ throw new Error(
78096
+ "Manpower monitoring settings for this site have no HRM Labs location name."
78097
+ );
78098
+ }
78099
+ return name;
78100
+ }
78092
78101
  function useHrmLabsAttendanceSrvc() {
78093
78102
  const { getManpowerSettingsBySiteId: _getManpowerSettingsBySiteId } = useManpowerMonitoringRepo();
78094
78103
  const { getManpowerDesignationsBySiteId: _getManpowerDesignationsBySiteId } = useManpowerDesignationRepo();
@@ -78096,7 +78105,6 @@ function useHrmLabsAttendanceSrvc() {
78096
78105
  const {
78097
78106
  startDate,
78098
78107
  endDate,
78099
- siteName,
78100
78108
  siteId,
78101
78109
  shiftName,
78102
78110
  search,
@@ -78126,6 +78134,7 @@ function useHrmLabsAttendanceSrvc() {
78126
78134
  message: "Manpower monitoring settings not found for the provided site ID."
78127
78135
  };
78128
78136
  }
78137
+ const siteName = locationOf(config2);
78129
78138
  const shiftType = config2.shiftType === "2-shifts" ? "2-shifts" : "3-shifts";
78130
78139
  const shiftData = config2.shifts[shiftType].find(
78131
78140
  (s) => s.name === shiftName
@@ -78195,7 +78204,7 @@ function useHrmLabsAttendanceSrvc() {
78195
78204
  }
78196
78205
  }
78197
78206
  async function getAttendanceDataCount(payload) {
78198
- const { startDate, endDate, siteName, siteId, search, serviceProviderId } = payload;
78207
+ const { startDate, endDate, siteId, search, serviceProviderId } = payload;
78199
78208
  const username = process.env.HRMLABS_USERNAME;
78200
78209
  const domain = process.env.HRMLABS_DOMAIN;
78201
78210
  const password = process.env.HRMLABS_PASSWORD;
@@ -78218,6 +78227,7 @@ function useHrmLabsAttendanceSrvc() {
78218
78227
  message: "Manpower monitoring settings not found for the provided site ID."
78219
78228
  };
78220
78229
  }
78230
+ const siteName = locationOf(config2);
78221
78231
  const shiftType = config2.shiftType;
78222
78232
  const shiftData = config2.shifts[shiftType];
78223
78233
  if (!shiftData) {
@@ -78308,7 +78318,7 @@ function useHrmLabsAttendanceSrvc() {
78308
78318
  }
78309
78319
  }
78310
78320
  async function getAllAttendance(payload) {
78311
- const { startDate, endDate, siteName, siteId, search, serviceProviderId } = payload;
78321
+ const { startDate, endDate, siteId, search, serviceProviderId } = payload;
78312
78322
  let { shiftName } = payload;
78313
78323
  const username = process.env.HRMLABS_USERNAME;
78314
78324
  const domain = process.env.HRMLABS_DOMAIN;
@@ -78331,6 +78341,7 @@ function useHrmLabsAttendanceSrvc() {
78331
78341
  message: "Manpower monitoring settings not found for the provided site ID."
78332
78342
  };
78333
78343
  }
78344
+ const siteName = locationOf(config2);
78334
78345
  const designationConfig = await _getManpowerDesignationsBySiteId(
78335
78346
  siteId,
78336
78347
  serviceProviderId
@@ -78482,7 +78493,6 @@ function useHrmLabsAttendanceSrvc() {
78482
78493
  const {
78483
78494
  startDate,
78484
78495
  endDate,
78485
- siteName,
78486
78496
  siteId,
78487
78497
  search,
78488
78498
  serviceProviderId,
@@ -78510,6 +78520,7 @@ function useHrmLabsAttendanceSrvc() {
78510
78520
  message: "Manpower monitoring settings not found for the provided site ID."
78511
78521
  };
78512
78522
  }
78523
+ const siteName = locationOf(config2);
78513
78524
  const designationConfig = await _getManpowerDesignationsBySiteId(
78514
78525
  siteId,
78515
78526
  serviceProviderId