easynet.run · 全部问题场景

我只允许 AI 查订单,它却还拿到了改价和退款权限。

客服想让 AI 回答“我的订单什么时候到”。为了接入后台,开发时用了一个现成管理员账号,它既能查进度,也能改价和退款。提示词里虽然写着“只查询”,但助手一旦调错操作,底层并没有另一道限制。

把“只查询”落实成四个可以检查的选择:哪个 Agent、哪项查询能力、哪个订单对象、允许到什么时候。EasyNet 的 authority.binding 把它们保存在一条授权记录里,提供方可以列出检查,也可以按 grant_id 撤销。

当原后台只能提供过宽的接口时,可以在它前面接出一个更具体的操作:查某个订单的配送进度。EasyRemote 能把这样的函数提供给不同助手使用,EasyNet 负责相应的调用入口;订单属于谁、哪些字段能返回,则需要由这个业务函数及原系统共同检查。

这个函数不应该接收“你想执行什么后台方法”,再替助手原样转发。它只接受查进度需要的条件,并依据已经确认的调用身份确定可见订单。支付信息、内部备注和退款操作不在这个工作范围内,就不应顺便出现在结果或接口中。

底层凭证也要尽可能收窄。如果原系统支持只读账号,提供方就使用只读账号;如果只能拿到宽权限凭证,需要明确它仍然是风险点,不能因为外面包了一层函数就宣称问题完全消失。凭证留在提供方,调用者不需要为了问一次进度而拿到它。

验收时除了正常查询,还应主动尝试改订单号、增加退款参数、要求返回更多字段。检查这些请求是在执行端被拒绝,还是仅仅没有出现在界面上。尤其要用不同身份测试:自己订单能查,不代表改成别人的订单也应该返回。

下面用现有 API 写一条一小时授权:接收者是完整 Agent URA,能力只选查询入口,订单作为 subject。列出记录核对后,任务结束就在 Access Control 设置页撤销,再试一次查询。给同事本人授权则使用 User 类型和 User URA,不把人和他运行的 Agent 当成同一个主体。下方是请求样本,不是实际订单系统的成功实录。

  1. 01

    客服询问进度

    输入订单号,不输入后台方法名。

  2. 02

    核对身份与归属

    执行端确认订单属于获准范围。

  3. 03

    只返回配送信息

    不附送付款、内部备注或退款入口。

把配送查询做成一个窄操作,而不是暴露后台方法集合。

只投影客服需要的字段

此本地投影只展示输出收窄;认证、订单归属和只读凭证仍需实现。

本地示例 · python
def delivery_fields(order):
    return {key: order[key] for key in ('id', 'delivery_status')}

print(delivery_fields({
    'id': 'demo-8', 'delivery_status': 'out_for_delivery',
    'internal_note': 'private', 'payment_token': 'not-returned'
}))

预期结果:结果仅含id和delivery_status,没有内部备注与支付字段。

把权限写成一条可以查阅和撤销的记录

例如,让订单查询 Agent 在接下来一小时内查询 ORDER-24,但不授予退款权限。通过 POST /api/v1/access-control/grants 创建授权,再到设置页查看或撤销。下面展示请求内容;提交时需要有治理权限的账号认证,以及可达的目标 Runtime。

示例地址、订单与时间都是合成数据。提交前,用实际目录中的目标、Agent、查询能力和业务认可的订单 subject 替换它们,生成新的 grant_id,并填写当前时间与截止时间。created_by 填当前认证会话对应的真实 User URA;请求不传 actor_ura,后端从认证会话取得操作人,填写身份字段不会赋予权限。

1. 选谁、哪项操作、哪个对象、到什么时候

principal_ura 是获得权限的 Agent;callee 是提供方。只给 lookup 这一项能力的 invoke 动作,限定 ORDER-24,并在一小时后到期。查询接口的 admission action 必须确实是 invoke;若实际描述为 read,就使用该真实动作。退款能力不在这条 grant 中;已有其他 grant 仍须一并检查。

{
  "operation": "grant",
  "target_ura": "easynet:///r/example/device/provider",
  "grant": {
    "grant_id": "example-order-reader-24",
    "owner_user_id": "owner",
    "owner_ura": "easynet:///r/example/user/owner",
    "principal_kind": "agent",
    "principal_id": "easynet:///r/example/agent/owner.order-reader",
    "principal_ura": "easynet:///r/example/agent/owner.order-reader",
    "callee_ura": "easynet:///r/example/agent/owner.orders",
    "subject_ura_pattern": "easynet:///r/example/resource/user.owner/order/ORDER-24",
    "ability_ura_pattern": "easynet:///r/example/ability/agent.owner.orders.lookup",
    "actions": [
      "invoke"
    ],
    "effect": "allow",
    "lifetime": "ttl",
    "state": "active",
    "created_by": "easynet:///r/example/user/owner",
    "created_at": "2026-09-07T10:00:00Z",
    "expires_at": "2026-09-07T11:00:00Z",
    "reason": "One-hour order progress lookup"
  }
}

2. 列出后确认,不把请求当作成功

检查返回的 grant_id、完整主体 URA、能力、对象、动作和期限。收到成功响应后也要实际试查询、退款和另一订单:这条记录不替业务接口检查订单归属。

{
  "operation": "list",
  "target_ura": "easynet:///r/example/device/provider",
  "owner_ura": "easynet:///r/example/user/owner",
  "principal_kind": "agent",
  "principal_ura": "easynet:///r/example/agent/owner.order-reader",
  "state": "active"
}

3. 撤销同一条记录

使用列表返回的真实 grant_id 撤销,再查询 revoked 状态。设置 → Access Control 也能执行同一撤销操作。最后再次发起查询检查拒绝结果;撤销不意味着此前已运行的任务自动取消。

{
  "operation": "revoke",
  "target_ura": "easynet:///r/example/device/provider",
  "owner_ura": "easynet:///r/example/user/owner",
  "grant_id": "example-order-reader-24",
  "reason": "Order lookup access withdrawn"
}

换成同事本人,不是改个显示名称

User 使用 user 类型与完整 User URA,principal_id 是其真实 user id;Agent 使用 agent 类型,principal_id 与 principal_ura 都是完整 Agent URA。realm 是地址中的范围,不是这里的第三种 principal_kind。

{
  "principal_kind": "user",
  "principal_id": "colleague",
  "principal_ura": "easynet:///r/example/user/colleague"
}
下载请求样本 JSON

让 Agent 帮你准备并验证这条授权

英文任务附件包含真实身份替换、授权前确认、允许与拒绝测试、撤销检查。复制文件不会自动发起授权。

下载 AGENTS.md
查看完整任务
# Give an order-query Agent one hour of access

Help the user let an existing Agent query one synthetic order without granting
refund access. Use a test order and a non-production environment. Do not create
a second device, rebuild the business service, or grant access just because you
received this file. Ask the owner to approve the exact recipient, action, order
and expiry before submitting the grant.

## Establish the real identities

Obtain the target Runtime device URA, owner User URA and user ID, recipient Agent
URA, provider Agent URA, and the published lookup Ability URA from the user's
existing directory/configuration. Preserve complete URAs, including their realms.
Read the published descriptor and the business adapter: confirm the admission
action and how the order is bound to the Invocation subject. An `order_id` inside
args alone does not prove that the policy checks that order.

If the lookup ability or subject binding does not exist, explain exactly what
must be implemented and stop before granting. Do not invent an address, claim
automatic realm-wide access, or silently replace a narrow subject with `*`.

## Prepare the request

The existing product endpoint is `POST /api/v1/access-control/grants`. Use the
user's configured EasyNet API origin and an approved authenticated client. In
the EasyNet frontend checkout, `grantAccessControl`, `listAccessControlGrants`
and `revokeAccessControlGrant` in `src/lib/api/easynet-access-control.ts` use the
shared authenticated client. That client sends a Bearer access token; browser
cookies alone are not its authentication contract. Never print, copy into this
document, or send tokens to another service. Do not invent a permission CLI.

Prepare this body with actual values. Generate a unique grant ID and fresh
RFC3339 timestamps exactly one hour apart. `invoke` below must match the real
descriptor's admission action; use `read` only if that is what it declares.

```json
{
  "operation": "grant",
  "target_ura": "<actual Runtime device URA>",
  "grant": {
    "grant_id": "<new unique ID>",
    "owner_user_id": "<owner user ID>",
    "owner_ura": "<owner User URA>",
    "principal_kind": "agent",
    "principal_id": "<recipient Agent URA>",
    "principal_ura": "<recipient Agent URA>",
    "callee_ura": "<provider Agent URA>",
    "subject_ura_pattern": "<exact test order subject URA>",
    "ability_ura_pattern": "<exact lookup Ability URA>",
    "actions": ["invoke"],
    "effect": "allow",
    "lifetime": "ttl",
    "state": "active",
    "created_by": "<authenticated operator User URA>",
    "created_at": "<current RFC3339 timestamp>",
    "expires_at": "<one hour later>",
    "reason": "One-hour test order lookup"
  }
}
```

The backend derives the actor from authentication. Do not send `actor_ura` or
construct delegation proofs, signatures or authority headers yourself. If the
recipient is a person, use `principal_kind: user`, their scalar user ID in
`principal_id`, and their full User URA in `principal_ura`. Realm is part of the
address, not a third principal kind.

## Verify what actually changed

First list the recipient's existing grants with operation `list`, the actual
`target_ura`, `owner_ura`, `principal_kind`, `principal_ura` and `state: active`.
Check for broader existing permissions without removing them automatically.
Submit the owner-approved grant, then list again and match the returned ID,
recipient, subject, ability, action and expiry. Save sanitized responses.

Use the recipient's real authenticated invocation path to test:

- The permitted order query returns the expected synthetic order data.
- A different order is denied before its data is returned.
- A different recipient does not gain access from this grant.
- Refund is denied in a dry-run/test adapter before any financial side effect.

Never send a real refund to prove denial. If there is no safe refund test path,
mark that check untested. Other grants may allow a call; do not attribute their
permission to this narrow grant. A stored record alone is not evidence of
end-to-end enforcement, cross-realm delegation, or receipt completeness.

## Revoke and check again

With the owner's approval, POST operation `revoke` using the same target and
owner, the actual returned `grant_id`, and a reason. The Access Control settings
page also supports revocation. List with `state: revoked`, then repeat the test
lookup and record whether admission rejects it. Revocation does not promise to
cancel work already running. Test expiry separately if claiming expiry works.

Report the exact successful and failed checks, sanitized response/receipt IDs,
and whether the test grant was revoked or remains active. Do not report success
when you only prepared JSON or encountered an authentication/authority error.
接入时的检查项
  • 允许查询能返回必要进度信息。
  • 调用者不能用参数切换到修改类操作。
  • 其他客户订单及禁止字段不被返回。
源码与接入资料