Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .github/workflows/elevation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ name: elevation
# 铁律对齐:
# - 默认拒绝:reason/spec_ref 缺失(HO 场景 3)、能力未声明、角色不匹配、
# TTL 越界 → deny(裁决逻辑唯一真源=governance/elevation.py,纯函数可测)。
# - 判定锚点机械(INV-01/02):role 取 GitHub author_association(OWNER→owner)
# 与 actor 身份(cloudbrid-agent[bot]→agent),workflow 不自判。
# - 判定锚点机械(INV-01/02):role 取 GitHub author_association(org 仓
# OWNER/MEMBER→owner——一人公司语境 MEMBER 即 owner 本人)与 actor 身份
# (cloudbrid-agent[bot]→agent),workflow 不自判。
# - append-only(ADR-0062):账本经 evidence_shadow.py 链式追加,验链后推送;
# 幂等=delivery_id(评论 id)已入账即 no-op(重复投递不双写)。
# - v0 边界:裁决+记账+TTL 收回闭环;不铸造真实平台凭证(提权档
Expand Down Expand Up @@ -62,10 +63,15 @@ jobs:
CARD="Cloudbird-Software/.github#${ISSUE_N}"
printf '%s\n' "$COMMENT_BODY" >"$TMP/comment.txt"

# role 判定锚点机械(INV-01):OWNER→owner;cloudbrid-agent App→agent;其余→none
if [[ "$AUTHOR_ASSOC" == "OWNER" ]]; then ROLE=owner
# role 判定锚点机械(INV-01):OWNER/MEMBER→owner(org 仓 owner 的
# author_association 呈 MEMBER——一人公司语境 MEMBER 即 owner 本人,
# 2026-08-29 实测);cloudbrid-agent App→agent;其余→none
if [[ "$AUTHOR_ASSOC" == "OWNER" || "$AUTHOR_ASSOC" == "MEMBER" ]]; then ROLE=owner

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Members gain owner authorization 🐞 Bug ⛨ Security

Mapping every MEMBER comment author to ROLE=owner gives all organization members the owner-only
elevation permissions, regardless of their repository or organization privileges. GitHub defines
MEMBER as any member of the owning organization, and the adjudicator directly grants owner
capabilities from this value.
Agent Prompt
## Issue description
`author_association=MEMBER` identifies any organization member, not specifically an organization owner, so treating it as `owner` widens elevation authorization.

## Issue Context
Organization-owned repositories do not provide sufficient owner privilege information through this association alone. Resolve the requester against an explicit trusted-owner allowlist or a GitHub API check that verifies organization-owner status before passing `role=owner`; otherwise use `none`.

## Fix Focus Areas
- .github/workflows/elevation.yml[66-74]
- governance/policy/elevation.yaml[20-34]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

elif [[ "$ACTOR" == "cloudbrid-agent[bot]" ]]; then ROLE=agent
else ROLE=none; fi
# 影子账本 actor.role 枚举(owner/agent/bot/human):裁决 role=none 的
# 请求方按事实身份记 human(none 非法枚举——实测 exit 3)
if [[ "$ROLE" == "none" ]]; then AROLE=human; else AROLE=$ROLE; fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Bot requesters recorded human 🐞 Bug ◔ Observability

The new none → human conversion records every unrecognized automation account as a human, because
bots other than cloudbrid-agent[bot] also receive ROLE=none. This corrupts the actor-role field
of deny events in the evidence ledger and makes machine-originated requests appear human-originated.
Agent Prompt
## Issue description
Audit-role normalization currently converts all unauthorized requesters to `human`, including GitHub App and bot accounts.

## Issue Context
Keep authorization role (`owner/agent/none`) separate from evidence actor type. Derive the audit role from `github.event.comment.user.type` or an equivalent reliable bot indicator, using `bot` for automation and `human` only for users.

## Fix Focus Areas
- .github/workflows/elevation.yml[69-75]
- .github/workflows/elevation.yml[111-116]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

echo "role=$ROLE (assoc=$AUTHOR_ASSOC actor=$ACTOR)"

# ---- 台账基线(clone elevation-ledger;不存在=创世) ----
Expand Down Expand Up @@ -103,7 +109,7 @@ jobs:
# ---- 台账追加(schema v1 链式 hash;kind=approval,AC-9c subject 可查询) ----
V=$(cat "$TMP/verdict.json")
jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg card "$CARD" \
--arg requester "$REQUESTER" --arg role "$ROLE" --argjson v "$V" \
--arg requester "$REQUESTER" --arg role "$AROLE" --argjson v "$V" \
'{ts:$ts, kind:"approval", action:("elevation."+$v.verdict), verdict:$v.verdict,
subject:{card:$card, tenant:"cloudbird-internal"},
actor:{identity:$requester, role:$role},
Expand Down Expand Up @@ -166,7 +172,7 @@ jobs:
'{ts:$ts, kind:"approval", action:"elevation.revoke", verdict:"revoked",
subject:{card:$g.card, tenant:"cloudbird-internal"},
actor:{identity:"elevation-bot", role:"bot"},
payload:{elevation_id:$g.elevation_id, capability:$g.capability, cause:"ttl-expired"}}' >event.json
payload:({elevation_id:$g.elevation_id, capability:$g.capability, cause:"ttl-expired"}|tojson)}' >event.json
python3 governance/evidence_shadow.py append --file "$LED" --event-file event.json
i=$((i + 1))
done
Expand Down