aws-runtime-bridge 1.9.180 → 1.9.181
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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ files 白名单),供调度中心「查看本次更新内容」弹窗按 `##
|
|
|
5
5
|
展示。发布新版本时须在仓库中先写好 `## v<version>` 段(prepack-guard 校验缺段
|
|
6
6
|
即阻断发布);Bridge 自更新成功后的运行时追加仅作为包内缺段时的兜底。
|
|
7
7
|
|
|
8
|
+
## v1.9.181
|
|
9
|
+
|
|
10
|
+
_发布日期:2026-11-01_
|
|
11
|
+
|
|
12
|
+
### 修复:插件等用户路由 403(agent-ownership-mismatch)
|
|
13
|
+
|
|
14
|
+
`requireUserScope` 的解析优先级纠正:validateToken 已按凭据
|
|
15
|
+
(connectionKey / runtime token)解析出的 `req.userScope` 必须原样保留,
|
|
16
|
+
不再被请求头/体无条件覆盖。此前有两个问题:
|
|
17
|
+
|
|
18
|
+
1. 携带合法凭据、但没带 `X-Tenant-Id` / `X-User-Id` 头的服务端调用
|
|
19
|
+
(如调度中心的插件代理)会被降级成 binding.json 的本机用户
|
|
20
|
+
`{tenantId:"_local"}`,与实例 owner.json 登记的真实归属永不相等,
|
|
21
|
+
`requireAgentOwnership` 一律拒绝——表现为插件面板一打开就报 403。
|
|
22
|
+
2. 更严重的是「凭据决定的权威 scope」可被「请求自称的 scope」替换,
|
|
23
|
+
伪造请求头即可跨 target / 跨租户写入别人的用户命名空间。
|
|
24
|
+
|
|
25
|
+
现在头/体身份仅服务于「无 target 凭据」的本机面板 / MCP 子进程链路,
|
|
26
|
+
凭据解析出的 scope 拥有最高优先级。
|
|
27
|
+
|
|
28
|
+
配套调度中心侧:`PluginProxyController` 发往 bridge 的代理请求补注
|
|
29
|
+
`X-Tenant-Id` / `X-User-Id`(按 agent owner,与 RuntimeBridgeService
|
|
30
|
+
同口径),本机面板/子进程链路不再缺身份头;插件面板错误提示优先透出
|
|
31
|
+
bridge 返回的真实原因,不再被 "Request failed with status code 403"
|
|
32
|
+
之类的 HTTP 概括语吞掉。
|
|
33
|
+
|
|
8
34
|
## v1.9.180
|
|
9
35
|
|
|
10
36
|
_发布日期:2026-11-01_
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomBytes as N}from"node:crypto";import{existsSync as
|
|
1
|
+
import{randomBytes as N}from"node:crypto";import{existsSync as B,mkdirSync as x,readFileSync as E,writeFileSync as K}from"node:fs";import R from"node:os";import A from"node:path";import P from"axios";import{getActiveBridgeHttpPort as k,getAutoRegisterConfigFilePath as b,schedulerBaseUrl as C}from"../config.js";import{runExclusiveConfigWrite as F}from"../utils/config-write-queue.js";import{logger as S}from"../utils/logger.js";import{normalizeTargetServerUrl as I}from"../utils/target-identity.js";const j="awsp_";function w(t){if(!B(t))return{};try{return JSON.parse(E(t,"utf-8"))}catch(s){throw new Error(`\u65E0\u6CD5\u89E3\u6790\u914D\u7F6E\u6587\u4EF6 ${t}: ${s.message}`)}}function $(t){return Array.isArray(t.autoRegisterTargets)?t.autoRegisterTargets.filter(s=>!!(s&&typeof s=="object"&&!Array.isArray(s))):[]}function q(){return`bridge-${N(6).toString("hex")}`}function O(t,s){return/^https?:\/\//i.test(t)?t:/:[0-9]+$/.test(t)?`http://${t}`:`http://${t}:${s}`}function _(t,s){const o=$(t),h=s.serverUrl?.trim(),e=h?I(h):"",m=String(s.instanceId||"").trim(),v=String(s.targetIdentity||"").trim();let n=-1,c=t,g=!1;if(o.length>0){const d=o.map((a,T)=>({item:a,itemIndex:T})).filter(({item:a})=>!e||I(a.serverUrl||a.schedulerBaseUrl)===e);let u=d;if(m){const a=d.filter(({item:T})=>String(T.instanceId||"").trim()===m);if(a.length>1)throw new Error(`\u5339\u914D\u5230\u591A\u4E2A instanceId=${m} \u7684\u914D\u5BF9\u76EE\u6807\uFF0C\u8BF7\u68C0\u67E5 config.json`);a.length===1?u=a:(u=[],g=!0)}else if(v){if(u=d.filter(({item:a})=>String(a.credentialId||a.userKey||"").trim()===v),u.length>1)throw new Error("\u5339\u914D\u5230\u591A\u4E2A\u914D\u5BF9\u76EE\u6807\uFF0C\u8BF7\u7528 --instance-id \u6216 --target-identity \u7CBE\u786E\u6307\u5B9A");if(u.length===0)throw new Error("\u6307\u5B9A targetIdentity \u7684\u914D\u5BF9\u76EE\u6807\u4E0D\u5B58\u5728")}else if(d.length>1)throw new Error("\u5B58\u5728\u591A\u4E2A\u6CE8\u518C\u76EE\u6807\uFF0C\u8BF7\u7528 --server-url \u642D\u914D --instance-id / --target-identity \u6307\u5B9A\u672C\u6B21\u914D\u5BF9\u76EE\u6807");if(g)n=o.length;else if(n=u[0]?.itemIndex??-1,n<0){if(!e)throw new Error("\u65E0\u6CD5\u786E\u5B9A\u914D\u5BF9\u76EE\u6807\uFF0C\u8BF7\u7528 --server-url \u6307\u5B9A");c={serverUrl:h},n=o.length,g=!0}else c=o[n]||{}}const y=I(h||c.serverUrl||c.schedulerBaseUrl||C);if(!y)throw new Error("\u65E0\u6CD5\u786E\u5B9A\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u8BF7\u7528 --server-url \u6307\u5B9A");const i=s.registerAddr||String(c.registerAddr||c.registerIp||c.virtualIp||"").trim(),l=c.tunnel===!0?!0:c.tunnel===!1?!1:!i;let r=m||String(c.instanceId||"").trim()||void 0,f;if(l){r||(r=q());const d=new URL(y);f=`${d.protocol}//${d.host}/tunnel/${r}`}else f=O(i,k());const p=String(c.connectionKey||"").trim(),U=s.instanceName?.trim()||String(c.instanceName||"").trim()||R.hostname();return{index:n,target:c,serverUrl:y,tunnel:l,instanceId:r,connectionKey:p,instanceName:U,runtimeBridgeBaseUrl:f,isNewTarget:g}}async function M(t,s){return(await P.post(t,s,{headers:{"Content-Type":"application/json"},timeout:15e3})).data}async function V(t){const s=String(t.ticket||"").trim();if(!s.startsWith(j))return{success:!1,error:"\u914D\u5BF9\u7968\u636E\u683C\u5F0F\u4E0D\u6B63\u786E\uFF08\u5E94\u4EE5 awsp_ \u5F00\u5934\uFF09\uFF0C\u8BF7\u4ECE\u7BA1\u7406\u9875\u91CD\u65B0\u590D\u5236"};const o=t.configPath||b(),h=w(o);let e;try{e=_(h,t)}catch(i){return{success:!1,error:i.message}}const m={pairingTicket:s,instanceName:e.instanceName,runtimeBridgeBaseUrl:e.runtimeBridgeBaseUrl};e.instanceId&&(m.instanceId=e.instanceId);const v=t.postPair||M;let n;try{n=await v(`${e.serverUrl}/api/instances/pair`,m)}catch(i){const l=i;return{success:!1,error:l.response?.data?.message||l.message||"\u914D\u5BF9\u8BF7\u6C42\u5931\u8D25"}}if(!n?.success||!n.machineToken||!n.instanceId)return{success:!1,error:n?.message||"\u8C03\u5EA6\u4E2D\u5FC3\u672A\u8FD4\u56DE\u673A\u5668\u51ED\u636E"};const c=String(n.connectionKey||"").trim();if(!c)return{success:!1,error:"\u8C03\u5EA6\u4E2D\u5FC3\u672A\u8FD4\u56DE\u8FDE\u63A5\u5BC6\u94A5\uFF08server \u7248\u672C\u8FC7\u65E7\uFF1F\uFF09\uFF0C\u8BF7\u5347\u7EA7 server \u540E\u91CD\u8BD5"};const g=n.instanceId,y=n.instanceName||e.instanceName;try{await F(o,()=>{const i=w(o),l=$(i);let r;if(Array.isArray(i.autoRegisterTargets)&&l.length>0){const f=l.filter(a=>I(a.serverUrl||a.schedulerBaseUrl)===I(e.serverUrl)),p=String(e.target.userKey||"").trim(),U=e.instanceId?f.filter(a=>String(a.instanceId||"").trim()===e.instanceId):[],d=p?f.filter(a=>String(a.userKey||"").trim()===p):[];if(U.length>1||d.length>1)throw new Error(`\u914D\u5BF9\u5199\u56DE\u5931\u8D25\uFF1A\u5339\u914D\u5230\u591A\u4E2A\u76EE\u6807\uFF08server=${e.serverUrl}\uFF09\uFF0C\u8BF7\u7528 instanceId / target identity \u7CBE\u786E\u6307\u5B9A`);const u=U[0]||d[0]||(f.length===1&&!e.isNewTarget?f[0]:void 0);u?r=u:!e.isNewTarget&&e.index>=0&&l[e.index]?r=l[e.index]:(r={serverUrl:e.serverUrl},l.push(r),i.autoRegisterTargets=l)}else r=i;r.serverUrl=r.serverUrl||e.serverUrl,r.instanceName=r.instanceName||y,r.instanceId=g,r.credentialId=g,delete r.credentialRef,r.credentialType=n.credentialType||"MACHINE_TOKEN",r.machineToken=n.machineToken,r.connectionKey=c,n.tenantId&&(r.tenantId=n.tenantId),n.userId&&(r.userId=n.userId),Array.isArray(i.autoRegisterTargets)&&i.autoRegisterTargets.length>0&&String(i.connectionKey||"").trim()&&delete i.connectionKey,x(A.dirname(o),{recursive:!0}),K(o,JSON.stringify(i,null,2),"utf-8")})}catch(i){return{success:!1,error:i.message}}return e.instanceId&&e.instanceId!==g&&S.info(`[Pair] server \u5DF2\u6309\u7968\u636E\u786E\u5B9A\u5B9E\u4F8B id\uFF1A${g}\uFF08\u672C\u5730\u6B64\u524D\u4E3A ${e.instanceId}\uFF09\uFF0Cconfig.json \u5DF2\u5199\u56DE\u65B0 id`),S.info(`[Pair] \u914D\u5BF9\u6210\u529F: server=${e.serverUrl}, instanceId=${g}, machineToken \u5DF2\u5199\u5165 ${o}`),{success:!0,instanceId:g,serverUrl:e.serverUrl}}export{V as pairBridge,_ as resolvePairTarget};
|
|
2
2
|
|
|
@@ -107,6 +107,17 @@ export declare function resolveUserScope(req: Request, _res: Response, next: Nex
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
110
121
|
export declare function requireUserScope(req: Request, res: Response, next: NextFunction): void;
|
|
111
122
|
|
|
112
123
|
export declare function getUserScopeOrThrow(req: Request): UserScope;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import n from"node:path";import{getBridgeDataHome as I}from"../config.js";import{loadRuntimeBinding as x}from"./runtime-binding.js";const m=/^[a-zA-Z0-9_.-]+$/,c=Object.freeze({tenantId:"_legacy",userId:"_legacy"}),y="x-tenant-id",U="x-user-id";function s(e){const r=String(e??"").replace(/[^a-zA-Z0-9_.-]/g,"_");return/^\.+$/.test(r)?"_":r.replace(/^\.+/g,"")}function w(e){return typeof e=="string"&&e.length>0&&m.test(e)}function p(e,r){const t=String(e??"").trim(),o=String(r??"").trim();return!t||!o?null:{tenantId:t,userId:o}}function D(e,r){return!e||!r?!1:s(e.tenantId)===s(r.tenantId)&&s(e.userId)===s(r.userId)}function b(e){return D(e,c)}function l(){return String(process.env.AWS_BRIDGE_LEGACY_SCOPE||"").trim().toLowerCase()==="1"}function L(){return String(process.env.AWS_BRIDGE_DISABLE_LEGACY_FALLBACK||"").trim().toLowerCase()==="1"}function _(e){return[s(e.tenantId),s(e.userId)]}function i(e){return n.join(I(),"users",..._(e))}function R(e){return n.join(i(e),"ai","acode.json")}function j(e){return n.join(i(e),"skills")}function O(e){return n.join(j(e),"deployed")}function d(e){return n.join(i(e),"mcp")}function P(e){return n.join(d(e),"deployed")}function B(e){return n.join(d(e),"packages")}function A(e){return n.join(i(e),"plugins")}function F(e){return n.join(A(e),"deployed")}function g(e,r){return n.join(i(e),"capability",r)}function T(e,r){return n.join(g(e,r),"config.json")}function z(e,r,t){return n.join(g(e,r),"agents",`${s(t)}.json`)}function E(...e){for(const r of e){const t=String(r??"").trim();if(t)return t}return""}function u(e){const r=p(e.header(y),e.header(U));if(r)return r;const t=e.body??{};return p(t.tenantId,E(t.ownerUserId,t.userId))}function S(){try{const e=x();return e?.userId?p("_local",e.userId):null}catch{return null}}function G(e,r,t){const o=u(e)??S();o?e.userScope=o:l()&&(e.userScope={...c}),t()}function v(e,r,t){const o=u(e)??S();if(o){e.userScope=o,t();return}if(l()){e.userScope={...c},t();return}r.status(400).json({ok:!1,error:"user-scope-required",message:"\u8BF7\u6C42\u7F3A\u5C11 X-Tenant-Id / X-User-Id\uFF08\u6216\u4F53 tenantId + ownerUserId\uFF09\u8EAB\u4EFD"})}function H(e){const r=e.userScope??u(e);if(!r)throw new Error("user-scope-required: \u8BF7\u6C42\u7F3A\u5C11 (tenantId, userId) \u8EAB\u4EFD");return r}let a=null;function N(e){a=e}function M(e,r,t){const o=String(e.params?.agentId??e.body?.agentId??"").trim();if(!o){t();return}const f=e.userScope??u(e);if(!f){r.status(400).json({ok:!1,error:"user-scope-required",message:"agent \u7EA7\u64CD\u4F5C\u7F3A\u5C11 (tenantId, userId) \u8EAB\u4EFD"});return}if(a&&!a(o,f)){r.status(403).json({ok:!1,error:"agent-ownership-mismatch",message:"\u8BE5\u5B9E\u4F8B\u4E0D\u5C5E\u4E8E\u5F53\u524D\u7528\u6237"});return}t()}export{c as LEGACY_SCOPE,m as SCOPE_ID_PATTERN,y as TENANT_HEADER,U as USER_HEADER,u as extractUserScope,R as getUserAiConfigFile,z as getUserCapabilityAgentFile,T as getUserCapabilityDefaultsFile,g as getUserCapabilityDir,P as getUserMcpDeployedDir,d as getUserMcpDir,B as getUserMcpPackagesDir,F as getUserPluginsDeployedDir,A as getUserPluginsDir,H as getUserScopeOrThrow,i as getUserScopeRoot,O as getUserSkillsDeployedDir,j as getUserSkillsDir,L as isLegacyFallbackDisabled,b as isLegacyScope,l as isLegacyScopeAllowed,w as isValidScopeId,p as normalizeUserScope,M as requireAgentOwnership,v as requireUserScope,G as resolveUserScope,s as sanitizeScopeId,D as scopeEquals,N as setAgentOwnershipChecker};
|
|
1
|
+
import n from"node:path";import{getBridgeDataHome as I}from"../config.js";import{loadRuntimeBinding as x}from"./runtime-binding.js";const m=/^[a-zA-Z0-9_.-]+$/,c=Object.freeze({tenantId:"_legacy",userId:"_legacy"}),y="x-tenant-id",U="x-user-id";function s(e){const r=String(e??"").replace(/[^a-zA-Z0-9_.-]/g,"_");return/^\.+$/.test(r)?"_":r.replace(/^\.+/g,"")}function w(e){return typeof e=="string"&&e.length>0&&m.test(e)}function p(e,r){const t=String(e??"").trim(),o=String(r??"").trim();return!t||!o?null:{tenantId:t,userId:o}}function D(e,r){return!e||!r?!1:s(e.tenantId)===s(r.tenantId)&&s(e.userId)===s(r.userId)}function b(e){return D(e,c)}function l(){return String(process.env.AWS_BRIDGE_LEGACY_SCOPE||"").trim().toLowerCase()==="1"}function L(){return String(process.env.AWS_BRIDGE_DISABLE_LEGACY_FALLBACK||"").trim().toLowerCase()==="1"}function _(e){return[s(e.tenantId),s(e.userId)]}function i(e){return n.join(I(),"users",..._(e))}function R(e){return n.join(i(e),"ai","acode.json")}function j(e){return n.join(i(e),"skills")}function O(e){return n.join(j(e),"deployed")}function d(e){return n.join(i(e),"mcp")}function P(e){return n.join(d(e),"deployed")}function B(e){return n.join(d(e),"packages")}function A(e){return n.join(i(e),"plugins")}function F(e){return n.join(A(e),"deployed")}function g(e,r){return n.join(i(e),"capability",r)}function T(e,r){return n.join(g(e,r),"config.json")}function z(e,r,t){return n.join(g(e,r),"agents",`${s(t)}.json`)}function E(...e){for(const r of e){const t=String(r??"").trim();if(t)return t}return""}function u(e){const r=p(e.header(y),e.header(U));if(r)return r;const t=e.body??{};return p(t.tenantId,E(t.ownerUserId,t.userId))}function S(){try{const e=x();return e?.userId?p("_local",e.userId):null}catch{return null}}function G(e,r,t){const o=u(e)??S();o?e.userScope=o:l()&&(e.userScope={...c}),t()}function v(e,r,t){if(e.userScope){t();return}const o=u(e)??S();if(o){e.userScope=o,t();return}if(l()){e.userScope={...c},t();return}r.status(400).json({ok:!1,error:"user-scope-required",message:"\u8BF7\u6C42\u7F3A\u5C11 X-Tenant-Id / X-User-Id\uFF08\u6216\u4F53 tenantId + ownerUserId\uFF09\u8EAB\u4EFD"})}function H(e){const r=e.userScope??u(e);if(!r)throw new Error("user-scope-required: \u8BF7\u6C42\u7F3A\u5C11 (tenantId, userId) \u8EAB\u4EFD");return r}let a=null;function N(e){a=e}function M(e,r,t){const o=String(e.params?.agentId??e.body?.agentId??"").trim();if(!o){t();return}const f=e.userScope??u(e);if(!f){r.status(400).json({ok:!1,error:"user-scope-required",message:"agent \u7EA7\u64CD\u4F5C\u7F3A\u5C11 (tenantId, userId) \u8EAB\u4EFD"});return}if(a&&!a(o,f)){r.status(403).json({ok:!1,error:"agent-ownership-mismatch",message:"\u8BE5\u5B9E\u4F8B\u4E0D\u5C5E\u4E8E\u5F53\u524D\u7528\u6237"});return}t()}export{c as LEGACY_SCOPE,m as SCOPE_ID_PATTERN,y as TENANT_HEADER,U as USER_HEADER,u as extractUserScope,R as getUserAiConfigFile,z as getUserCapabilityAgentFile,T as getUserCapabilityDefaultsFile,g as getUserCapabilityDir,P as getUserMcpDeployedDir,d as getUserMcpDir,B as getUserMcpPackagesDir,F as getUserPluginsDeployedDir,A as getUserPluginsDir,H as getUserScopeOrThrow,i as getUserScopeRoot,O as getUserSkillsDeployedDir,j as getUserSkillsDir,L as isLegacyFallbackDisabled,b as isLegacyScope,l as isLegacyScopeAllowed,w as isValidScopeId,p as normalizeUserScope,M as requireAgentOwnership,v as requireUserScope,G as resolveUserScope,s as sanitizeScopeId,D as scopeEquals,N as setAgentOwnershipChecker};
|
|
2
2
|
|