P0 security batch: true global admin, quota gates on all spending paths, default quota for quota-less users, fix-review author trust + docker/owner attribution, GitHub App setup issuer binding, real cancel kill, wider shell env exclusion

- CODEX_GLOBAL_ADMINS replaces maintainer-derived global admin;
  admin endpoints (quotas, sweep, backup, push dispatch) fail-closed
  403 when unset, with audit on allow and deny.
- continue/followups/messages/retry check both caller and original
  creator quotas (USD + unpriced token cap), 402 on either overrun.
- Quota-less users now get the #86 default quota instead of unlimited;
  init_db backfills and OAuth create/bind/existing-login branches
  INSERT OR IGNORE a default row; explicit NULL row stays unlimited.
  user_quotas added to migration tables.
- /fix-review: untrusted authors rejected before dedup claim, no task;
  trusted tasks force executor=docker, created_by=project owner, owner
  quota gate, prompt is a fixed template plus a 2000-char excerpt.
- GitHub App setup state only issued by global admins, state rows bind
  issued_by (ALTER-compatible for old DBs), callback rejects issuers
  no longer in the admin list without consuming the state.
- Cancel: popen poll checks DB status every second, Docker path runs a
  cancel watchdog that kills by container name, write-back is CAS-guarded
  so late results never overwrite a new status.
- SHELL_ENV_EXCLUDE adds *_TOKEN, *_AUTH*, *_CREDENTIAL*, *API_KEY*;
  comment states the residual risk: same-UID /proc environ remains
  readable, true isolation needs UID separation or hidepid.
- Tests: new cloud/tests/test_p0_security.py (17 cases); old tests
  updated to the new semantics (admin env, default quota, trusted
  fix-review author) without weakening any gate.
This commit is contained in:
host committed 2026-10-27 15:35:23 +00:00
1 parent d0ca1f7f60
commit 7091a2d0d2
13 files changed
+1017 -146

No files matched your search

+61 -10
View File
@@ -92,9 +92,12 @@ def build_app_manifest(public_base, app_name=DEFAULT_APP_NAME,
class SetupStateStore:
"""setup state 存储:一次性、1 小时过期,防回调被冒用。
表 github_app_setup_states:state(主键)、created_at。
state 只由持本站 key 调 POST /github-app/setup/state 的
操作方签发;回调验到即删,同一 state 第二次回调必然失败。
表 github_app_setup_states:state(主键)、created_at、
issued_by(签发人用户 id,P0 安全批新增)。新表建表即
含该列;老库在 __init__ 里用 ALTER 补列(列已存在时
报错直接忽略,老库不炸,见 except 分支)。
state 只由全局管理员调 POST /github-app/setup/state
签发;回调验到即删,同一 state 第二次回调必然失败。
"""
def __init__(self, db_path):
@@ -102,38 +105,86 @@ class SetupStateStore:
self.conn = _db_connect(self.db_path)
self.conn.execute(
"CREATE TABLE IF NOT EXISTS github_app_setup_states("
"state TEXT PRIMARY KEY, created_at REAL)"
"state TEXT PRIMARY KEY, created_at REAL,"
" issued_by TEXT)"
)
self.conn.commit()
try:
self.conn.execute(
"ALTER TABLE github_app_setup_states"
" ADD COLUMN issued_by TEXT")
self.conn.commit()
except Exception:
try:
self.conn.rollback()
except Exception:
pass
def create_state(self):
def create_state(self, issued_by=None):
state = secrets.token_urlsafe(32)
self.conn.execute(
"INSERT INTO github_app_setup_states(state, created_at)"
" VALUES(?, ?)",
(state, time.time()),
"INSERT INTO github_app_setup_states"
"(state, created_at, issued_by)"
" VALUES(?, ?, ?)",
(state, time.time(), issued_by),
)
self.conn.commit()
return state
def state_issuer(self, state):
"""查 state 的签发人(回调审计用,不消耗 state)。"""
if not state:
return None
try:
row = self.conn.execute(
"SELECT issued_by FROM github_app_setup_states"
" WHERE state = ?",
(state,),
).fetchone()
except Exception:
return None
if row is None:
return None
try:
return row[0]
except Exception:
return None
def consume_state(self, state, now=None,
ttl_seconds=SETUP_STATE_TTL_SECONDS):
ttl_seconds=SETUP_STATE_TTL_SECONDS,
allowed_issuers=None):
"""校验并消耗 state:成功返回 True,失败返回 False。
不存在 -> False;过期 -> 删行并返回 False;成功 -> 删行
并返回 True。``now`` 只为单测注入时间。
P0 安全批后:allowed_issuers 非空时,签发人不在该
集合里一律返回 False 且不消耗 state(fail-closed);
签发人为空(升级前的旧 state)同样不在集合里,等
于升级后旧 state 全部作废,需管理员重新签发。注意:
回调本身是浏览器无认证重定向,服务器无法从回调请求
里认出调用方就是签发人,所以这里核对的是“签发人仍
在管理员清单里”,不是“回调者就是签发人”——后者
在本流程里做不到,文档与 PR 描述里必须如实写清,
不能谎称回调做了身份认证。
"""
if not state:
return False
if now is None:
now = time.time()
row = self.conn.execute(
"SELECT created_at FROM github_app_setup_states"
"SELECT created_at, issued_by FROM github_app_setup_states"
" WHERE state = ?",
(state,),
).fetchone()
if row is None:
return False
if allowed_issuers is not None:
try:
issuer = row[1]
except Exception:
issuer = None
if issuer not in allowed_issuers:
return False
self.conn.execute(
"DELETE FROM github_app_setup_states WHERE state = ?",
(state,),