@dsh-plus/access-gate 0.3.5 → 0.3.6

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/lib/index.js CHANGED
@@ -726,7 +726,7 @@ function installGateInterceptor(ctx, server, deps) {
726
726
  wrapped.clear();
727
727
  } });
728
728
  const undo = () => {
729
- for (let i = restores.length - 1; i >= 0; i -= 1) restores[i].undo();
729
+ for (const restore of [...restores].reverse()) restore.undo();
730
730
  };
731
731
  return undo;
732
732
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/access-gate",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "dsh-plus service+ui plugin: dsh web 全量访问围栏——与官方 browser-auth 合并:官方 cookie 为唯一凭据,可选 IP 附加围栏,未认证导航渲染 token 输入页(PWA 可恢复),loopback 管理通道",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -31,7 +31,7 @@
31
31
  }
32
32
  },
33
33
  "dependencies": {
34
- "@dsh-plus/shared": "0.1.14"
34
+ "@dsh-plus/shared": "0.1.15"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@deepseek-ai/cordis": "^4.0.2",
package/src/cookie.ts CHANGED
@@ -74,11 +74,29 @@ function validSignature(value: string, secret: Buffer): boolean {
74
74
  return actual.byteLength === expected.byteLength && timingSafeEqual(actual, expected)
75
75
  }
76
76
 
77
+ /** cookie 载荷的三字段(官方 encodeCookie 的键集;version 由形状守卫单判)。 */
78
+ interface CookiePayload {
79
+ authority: string
80
+ issuedAt: number
81
+ expiresAt: number
82
+ }
83
+
84
+ /**
85
+ * 载荷形状守卫(类型谓词):version 必须等于 PAYLOAD_VERSION,三字段类型正确。
86
+ * 逐字段 `in` + typeof 收窄取代裸断言——JSON.parse 回来的是 unknown,
87
+ * 只有守卫通过后才允许按 CookiePayload 访问。
88
+ */
89
+ function isCookiePayload(value: unknown): value is CookiePayload {
90
+ if (typeof value !== 'object' || value === null) return false
91
+ if (!('version' in value) || value.version !== PAYLOAD_VERSION) return false
92
+ if (!('authority' in value) || typeof value.authority !== 'string') return false
93
+ if (!('issuedAt' in value) || typeof value.issuedAt !== 'number') return false
94
+ if (!('expiresAt' in value) || typeof value.expiresAt !== 'number') return false
95
+ return Number.isSafeInteger(value.issuedAt) && Number.isSafeInteger(value.expiresAt)
96
+ }
97
+
77
98
  /** 解码 cookie 载荷(返回 null = 值损坏/签名不符/字段缺失,语义与官方一致)。 */
78
- export function decodeCookiePayload(
79
- value: string,
80
- secret: Buffer,
81
- ): { authority: string; issuedAt: number; expiresAt: number } | null {
99
+ export function decodeCookiePayload(value: string, secret: Buffer): CookiePayload | null {
82
100
  if (!validSignature(value, secret)) return null
83
101
  const body = value.split('.')[1]
84
102
  if (body === undefined) return null
@@ -88,22 +106,8 @@ export function decodeCookiePayload(
88
106
  } catch {
89
107
  return null
90
108
  }
91
- if (
92
- typeof decoded !== 'object' ||
93
- decoded === null ||
94
- !('version' in decoded) ||
95
- (decoded as { version?: unknown }).version !== PAYLOAD_VERSION ||
96
- typeof (decoded as { authority?: unknown }).authority !== 'string' ||
97
- !Number.isSafeInteger((decoded as { issuedAt?: unknown }).issuedAt) ||
98
- !Number.isSafeInteger((decoded as { expiresAt?: unknown }).expiresAt)
99
- ) {
100
- return null
101
- }
102
- const { authority, issuedAt, expiresAt } = decoded as {
103
- authority: string
104
- issuedAt: number
105
- expiresAt: number
106
- }
109
+ if (!isCookiePayload(decoded)) return null
110
+ const { authority, issuedAt, expiresAt } = decoded
107
111
  const now = Date.now()
108
112
  if (issuedAt > now || expiresAt <= now || expiresAt <= issuedAt) return null
109
113
  if (expiresAt - issuedAt > COOKIE_MAX_AGE_MS) return null
@@ -293,7 +293,8 @@ export function installGateInterceptor(
293
293
  })
294
294
 
295
295
  const undo = (): void => {
296
- for (let i = restores.length - 1; i >= 0; i -= 1) restores[i].undo()
296
+ // 逆序还原(后装先撤);forEach 直接取项,免去索引访问的 undefined 分支。
297
+ for (const restore of [...restores].reverse()) restore.undo()
297
298
  }
298
299
  void ctx
299
300
  return undo