@dshtrading/authority 0.6.0 → 0.6.1
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.d.ts +22 -2
- package/lib/index.js +23 -7
- package/package.json +2 -2
package/lib/index.d.ts
CHANGED
|
@@ -106,13 +106,32 @@ interface AuthorityOptions {
|
|
|
106
106
|
/**
|
|
107
107
|
* 判定进程的 euid,缺省 process.geteuid()。测试用来**模拟生产形态**(平面归另一个
|
|
108
108
|
* uid、进程跑在 agent uid 下)——本机同 uid 时无法真实制造这种文件属主。
|
|
109
|
+
*
|
|
110
|
+
* 只在有 uid 语义的平台上有效:见 uidSemantics。
|
|
109
111
|
*/
|
|
110
112
|
euid?: number;
|
|
113
|
+
/**
|
|
114
|
+
* 本平台是否有 uid 语义(`process.geteuid` 可用);缺省 hasUidSemantics()。
|
|
115
|
+
*
|
|
116
|
+
* 测试用 false 表达「没有 uid 语义的平台(Windows)」。此时注入 euid 也不构成隔离证据
|
|
117
|
+
* —— Windows 的 `fs.Stats.uid` 恒为 0、`mode` 是按只读属性合成的,拿它们比对只会把
|
|
118
|
+
* 「无从证明」说成某个具体结论。
|
|
119
|
+
*/
|
|
120
|
+
uidSemantics?: boolean;
|
|
111
121
|
}
|
|
112
122
|
/** 显式 opt-in 的开发形态(名字里带 dev,且值必须是精确的 '1')。 */
|
|
113
123
|
declare function devOptIn(env?: Record<string, string | undefined>): boolean;
|
|
114
124
|
/** 当前进程的 euid;没有 uid 语义的平台(Windows)返回 undefined ⇒ 读取端按未隔离拒绝。 */
|
|
115
125
|
declare function processEuid(): number | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* 本平台是否有 uid 语义(`process.geteuid` 可用)——属主检查的**能力前提**。
|
|
128
|
+
*
|
|
129
|
+
* Windows 上没有:`fs.Stats.uid` 恒为 0、`mode` 是按只读属性合成的,两者都不是属主证据。
|
|
130
|
+
* 「平面归另一个 uid 所有」在 Windows 上无从证明,所以判定按能力而非按注入值走 —— 否则拿
|
|
131
|
+
* 合成的 stat 去比对只会得到 `ancestor-agent-writable` 这类假阳性结论(把「不知道」说成
|
|
132
|
+
* 「agent 可写」)。没有这条语义就一定 fail-closed。
|
|
133
|
+
*/
|
|
134
|
+
declare function hasUidSemantics(): boolean;
|
|
116
135
|
/**
|
|
117
136
|
* 授权平面根目录:显式 dir > $DSH_TRADING_AUTHORITY_DIR > **未配置(undefined)**。
|
|
118
137
|
*
|
|
@@ -155,7 +174,8 @@ interface PlaneInspection {
|
|
|
155
174
|
}
|
|
156
175
|
/**
|
|
157
176
|
* 判定一个平面目录:先做隔离检查,再验签(两者都按 stat 指纹缓存,文件/属主一变立刻重算)。
|
|
158
|
-
* 目录不存在时隔离检查返回 dir-missing
|
|
177
|
+
* 目录不存在时隔离检查返回 dir-missing,验签照旧走到「没有信任锚」——该结论在有 uid 语义的
|
|
178
|
+
* 平台上成立;无 uid 语义的平台(Windows)在能力门就先返回 no-uid-semantics。
|
|
159
179
|
*/
|
|
160
180
|
declare function inspectPlane(dir: string, options?: AuthorityOptions): PlaneInspection;
|
|
161
181
|
/** 判定平面当前状态(按 stat 指纹缓存,文件一变立刻重算)。dev 形态授权缺省被拒。 */
|
|
@@ -175,4 +195,4 @@ declare function liveTradingEnabled(mirror: boolean | undefined, options?: Autho
|
|
|
175
195
|
/** 清空 stat 指纹缓存(测试与「删掉平面后立刻复验」的场景用)。 */
|
|
176
196
|
declare function resetAuthorityCache(): void;
|
|
177
197
|
//#endregion
|
|
178
|
-
export { AUTHORITY_AGENT_UID_ENV, AUTHORITY_DEV_ENV, AUTHORITY_DIR_ENV, AUTHORITY_OWNER_UID_ENV, AuthorityDirSource, AuthorityLocation, AuthorityOptions, GRANT_FILENAME, GRANT_PROTOCOL_VERSION, GrantPayload, GrantVerification, LiveTradingDecision, LiveTradingDenyReason, PlaneInspection, PlaneIsolationCode, PlaneIsolationReport, TRUSTED_KEYS_FILENAME, TrustedKey, VerifyOptions, authorityDir, canonicalize, devOptIn, inspectPlane, inspectPlaneIsolation, liveTradingDecision, liveTradingEnabled, parseTrustedKeys, processEuid, resetAuthorityCache, resolveAuthorityLocation, setAuthorityMismatchSink, verifyAtDirectory, verifyGrantDocument };
|
|
198
|
+
export { AUTHORITY_AGENT_UID_ENV, AUTHORITY_DEV_ENV, AUTHORITY_DIR_ENV, AUTHORITY_OWNER_UID_ENV, AuthorityDirSource, AuthorityLocation, AuthorityOptions, GRANT_FILENAME, GRANT_PROTOCOL_VERSION, GrantPayload, GrantVerification, LiveTradingDecision, LiveTradingDenyReason, PlaneInspection, PlaneIsolationCode, PlaneIsolationReport, TRUSTED_KEYS_FILENAME, TrustedKey, VerifyOptions, authorityDir, canonicalize, devOptIn, hasUidSemantics, inspectPlane, inspectPlaneIsolation, liveTradingDecision, liveTradingEnabled, parseTrustedKeys, processEuid, resetAuthorityCache, resolveAuthorityLocation, setAuthorityMismatchSink, verifyAtDirectory, verifyGrantDocument };
|
package/lib/index.js
CHANGED
|
@@ -25,6 +25,9 @@ import { basename, dirname, join, resolve } from "node:path";
|
|
|
25
25
|
* 文件、以及从根到平面目录的整条祖先链都不得归该 uid 所有(祖先归 agent ⇒ 它可以
|
|
26
26
|
* unlink 后替换整个平面),目录与文件还不得带 group/other 写位。任何一条不满足 ⇒
|
|
27
27
|
* plane-not-isolated。**这条是 agent 伪造不了的**:它写得进文件,改不了文件的属主。
|
|
28
|
+
* 平台前提是 POSIX 属主/权限语义:**没有 uid 语义的平台(Windows 上 process.geteuid
|
|
29
|
+
* 不可用,fs.Stats.uid 恒为 0、mode 按只读属性合成)无法证明隔离** ⇒ 一律
|
|
30
|
+
* no-uid-semantics 拒绝(fail-closed);此时注入 euid 也不构成证据(见 hasUidSemantics)。
|
|
28
31
|
* 3. 可选加固:$DSH_TRADING_AUTHORITY_OWNER_UID 把「允许的属主」钉成某个具体 uid
|
|
29
32
|
* (而不是「只要不是 agent 就行」);把它配成运行 uid 自身按配置错误拒绝。
|
|
30
33
|
* 4. 开发形态必须**显式 opt-in**:$DSH_TRADING_AUTHORITY_DEV_SAME_UID=1(名字带 dev),
|
|
@@ -67,6 +70,17 @@ function processEuid() {
|
|
|
67
70
|
const geteuid = process.geteuid;
|
|
68
71
|
return typeof geteuid === "function" ? geteuid() : void 0;
|
|
69
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* 本平台是否有 uid 语义(`process.geteuid` 可用)——属主检查的**能力前提**。
|
|
75
|
+
*
|
|
76
|
+
* Windows 上没有:`fs.Stats.uid` 恒为 0、`mode` 是按只读属性合成的,两者都不是属主证据。
|
|
77
|
+
* 「平面归另一个 uid 所有」在 Windows 上无从证明,所以判定按能力而非按注入值走 —— 否则拿
|
|
78
|
+
* 合成的 stat 去比对只会得到 `ancestor-agent-writable` 这类假阳性结论(把「不知道」说成
|
|
79
|
+
* 「agent 可写」)。没有这条语义就一定 fail-closed。
|
|
80
|
+
*/
|
|
81
|
+
function hasUidSemantics() {
|
|
82
|
+
return processEuid() !== void 0;
|
|
83
|
+
}
|
|
70
84
|
function parseUid(value) {
|
|
71
85
|
if (value === void 0 || value.trim().length === 0) return {};
|
|
72
86
|
const uid = Number(value.trim());
|
|
@@ -135,12 +149,12 @@ function inspectPlaneIsolation(dir, options = {}) {
|
|
|
135
149
|
devMode: true,
|
|
136
150
|
detail: "开发形态:显式设置了 " + AUTHORITY_DEV_ENV + "=1,跳过属主/权限检查 —— 这条判定不构成生产授权(每次判定都留痕)"
|
|
137
151
|
};
|
|
138
|
-
const euid = options.euid ?? processEuid();
|
|
152
|
+
const euid = options.uidSemantics ?? hasUidSemantics() ? options.euid ?? processEuid() : void 0;
|
|
139
153
|
if (euid === void 0) return {
|
|
140
154
|
isolated: false,
|
|
141
155
|
code: "no-uid-semantics",
|
|
142
156
|
devMode: false,
|
|
143
|
-
detail: "本平台没有 uid 语义(process.geteuid
|
|
157
|
+
detail: "本平台没有 uid 语义(process.geteuid 不可用:fs.Stats.uid 恒为 0、mode 按只读属性合成),无法证明平面不归 agent 所有 ⇒ 按未隔离拒绝(开发形态请显式设置 " + AUTHORITY_DEV_ENV + "=1)"
|
|
144
158
|
};
|
|
145
159
|
const pinned = parseUid(env[AUTHORITY_OWNER_UID_ENV]);
|
|
146
160
|
if (pinned.error !== void 0) return {
|
|
@@ -392,12 +406,13 @@ function stampOf(file) {
|
|
|
392
406
|
}
|
|
393
407
|
}
|
|
394
408
|
const cache = /* @__PURE__ */ new Map();
|
|
395
|
-
function planeKeyOf(euid, env) {
|
|
396
|
-
return String(euid) + "|" + String(env["DSH_TRADING_AUTHORITY_OWNER_UID"] ?? "") + "|" + String(devOptIn(env));
|
|
409
|
+
function planeKeyOf(euid, env, semantics = true) {
|
|
410
|
+
return String(semantics) + "|" + String(euid) + "|" + String(env["DSH_TRADING_AUTHORITY_OWNER_UID"] ?? "") + "|" + String(devOptIn(env));
|
|
397
411
|
}
|
|
398
412
|
/**
|
|
399
413
|
* 判定一个平面目录:先做隔离检查,再验签(两者都按 stat 指纹缓存,文件/属主一变立刻重算)。
|
|
400
|
-
* 目录不存在时隔离检查返回 dir-missing
|
|
414
|
+
* 目录不存在时隔离检查返回 dir-missing,验签照旧走到「没有信任锚」——该结论在有 uid 语义的
|
|
415
|
+
* 平台上成立;无 uid 语义的平台(Windows)在能力门就先返回 no-uid-semantics。
|
|
401
416
|
*/
|
|
402
417
|
function inspectPlane(dir, options = {}) {
|
|
403
418
|
const env = options.env ?? process.env;
|
|
@@ -407,7 +422,8 @@ function inspectPlane(dir, options = {}) {
|
|
|
407
422
|
const grantStamp = stampOf(grantFile);
|
|
408
423
|
const keysStamp = stampOf(keysFile);
|
|
409
424
|
const dirStamp = stampOf(dir);
|
|
410
|
-
const
|
|
425
|
+
const semantics = options.uidSemantics ?? hasUidSemantics();
|
|
426
|
+
const planeKey = planeKeyOf(options.euid ?? processEuid(), env, semantics);
|
|
411
427
|
const cached = cache.get(dir);
|
|
412
428
|
if (cached !== void 0 && cached.grantStamp === grantStamp && cached.keysStamp === keysStamp && cached.dirStamp === dirStamp && cached.planeKey === planeKey) {
|
|
413
429
|
const payload = cached.verification.payload;
|
|
@@ -518,4 +534,4 @@ function resetAuthorityCache() {
|
|
|
518
534
|
reportedDev.clear();
|
|
519
535
|
}
|
|
520
536
|
//#endregion
|
|
521
|
-
export { AUTHORITY_AGENT_UID_ENV, AUTHORITY_DEV_ENV, AUTHORITY_DIR_ENV, AUTHORITY_OWNER_UID_ENV, GRANT_FILENAME, GRANT_PROTOCOL_VERSION, TRUSTED_KEYS_FILENAME, authorityDir, canonicalize, devOptIn, inspectPlane, inspectPlaneIsolation, liveTradingDecision, liveTradingEnabled, parseTrustedKeys, processEuid, resetAuthorityCache, resolveAuthorityLocation, setAuthorityMismatchSink, verifyAtDirectory, verifyGrantDocument };
|
|
537
|
+
export { AUTHORITY_AGENT_UID_ENV, AUTHORITY_DEV_ENV, AUTHORITY_DIR_ENV, AUTHORITY_OWNER_UID_ENV, GRANT_FILENAME, GRANT_PROTOCOL_VERSION, TRUSTED_KEYS_FILENAME, authorityDir, canonicalize, devOptIn, hasUidSemantics, inspectPlane, inspectPlaneIsolation, liveTradingDecision, liveTradingEnabled, parseTrustedKeys, processEuid, resetAuthorityCache, resolveAuthorityLocation, setAuthorityMismatchSink, verifyAtDirectory, verifyGrantDocument };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dshtrading/authority",
|
|
3
3
|
"description": "Live-trading authority plane for dsh-trading: a fail-closed, human-signed (Ed25519) grant outside every agent-writable path. The preset asset's liveTrading flag is demoted to a non-authoritative mirror that can only deny (design doc §5 / invariant 13.3).",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "./lib/index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@dshtrading/dsh-home": "^0.6.
|
|
28
|
+
"@dshtrading/dsh-home": "^0.6.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^24.0.0",
|