EP09. "wp-rest-api-development REST API 开发审查"
🔒 登录后可标记已读register_rest_route()里那个permission_callback参数,你是不是也图省事写过__return_true?WP 5.5 之后这已经是强制要求,写操作漏了这一条就是 CRITICALwp-rest-api-development是jorgerosal/wordpress-skills这套 18 个 skill 里专门审查 WordPress REST API 的一个,覆盖路由注册、权限回调、参数校验、response 结构这几块- 核心原则一句话:每条路由都要有清楚的契约——明确的权限、经过校验的输入、可预期的输出,三者缺一都算问题
- 前置知识:先看过 EP01「wp-security-review 安全审查」,两者都会碰到权限/nonce 相关的判断,REST API 用的是
permission_callback这套模型,不是 nonce
重点内容
这个 Skill 是做什么的
wp-rest-api-development 是针对 WordPress 自定义 REST 路由的系统化审查,覆盖路由注册、controller class 设计、request 解析、schema 校验、response 格式、缓存考量、版本兼容性,报告固定带行号、严重度、对照代码示例。
适合用在:
- 审计
register_rest_route()的实现 - 审查自定义 API endpoint 或 controller class
- 检查
permission_callback实现是否够严谨 - 校验
WP_REST_Request的输入处理 - 评估 response 结构、HTTP 状态码、schema 是否对齐
- 为 headless / block 驱动应用设计带版本号的 endpoint
不适合用在:
- 没有 REST 焦点的通用插件架构(交给
wp-plugin-development) - 整个插件的全面安全审查(交给
wp-security-review) - WooCommerce 专属的 endpoint 审查(交给
wp-woocommerce-dev) - GraphQL 相关的指导
触发方式
| 方式 | 写法 | 说明 |
|---|---|---|
| 斜线指令(完整版) | /wp-rest-review [path] | 完整审查,结果按文件、按严重度分组 |
| 斜线指令(快速版) | /wp-rest [path] | 只抓高风险问题,速度快 |
| 自然语言 | 「审查这个自定义 REST endpoint」「check my register_rest_route implementation」 | Claude 判断意图符合就自动触发 |
📌 触发关键词包括 register_rest_route、permission_callback、WP_REST_Request、REST endpoint、API schema、REST controller、headless WordPress、API auth 等。
审查流程
- 识别 REST 情境:插件初始化里的路由回调、继承
WP_REST_Controller的 controller class、headless 前端整合场景、纯后台内部 API - 先检查路由注册:namespace 与版本号格式、HTTP method 是否语义对应、
permission_callback有没有出现且够具体、args有没有定义 - 审查 request 处理:用
$request->get_param()或对应类型的 getter,而不是原生 superglobal;所有用户输入都要校验/消毒;不合法输入要回WP_Error - 审查 response 设计:结构统一、状态码合适、用
rest_ensure_response()、不暴露内部实现细节 - 按严重度分类(见下表)
- 交叉引用其他 skill:权限/nonce 问题突出时建议
/wp-sec-review;牵涉更大插件架构时建议/wp-plugin-review
按检查对象分类的坑
| 检查对象 | CRITICAL | WARNING | INFO |
|---|---|---|---|
路由注册 register_rest_route | 缺 permission_callback、写操作用 __return_true | namespace 没带版本号、路由注册在 rest_api_init 之外 | 重复出现的 inline callback 适合重构成 controller method |
| 权限回调 | 私有数据缺 capability 检查 | 特权操作的回调无条件回 true、没做归属校验 | 重复的权限逻辑可以集中处理 |
| Request args / 校验 | 回调里直接用 $_GET/$_POST | 缺 sanitize_callback/validate_callback、缺枚举/格式限制 | 可复用的 item schema 能减少重复 |
| Response 处理 | — | 各 endpoint 的成功 response 结构不统一、REST 回调里用 wp_send_json() | WP_REST_Response 能更好地控制 header 和状态码 |
快速扫描指令(rg)
# CRITICAL:定位所有 register_rest_route,检查是否有 __return_true 的权限回调,以及回调里直接用了原生 superglobal
rg -n "register_rest_route\s*\(" . -g '*.php'
rg -n "permission_callback.*__return_true" . -g '*.php'
rg -n "\$_GET|\$_POST|\$_REQUEST" . -g '*.php'
# WARNING:检查有没有用校验 helper、有没有该用 REST response 却用了 wp_send_json
rg -n "WP_REST_Request|get_param\s*\(" . -g '*.php'
rg -n "wp_send_json|wp_send_json_success|wp_send_json_error" . -g '*.php'
实操示例
// ❌ CRITICAL:没写 permission_callback,等于任何人都能打这条路由
register_rest_route( 'myplugin/v1', '/settings', array(
'methods' => 'POST',
'callback' => 'myplugin_save_settings',
) );
// ❌ CRITICAL:写操作用 __return_true,等于对外公开写权限
register_rest_route( 'myplugin/v1', '/settings', array(
'methods' => 'POST',
'callback' => 'myplugin_save_settings',
'permission_callback' => '__return_true',
) );
// ✅ GOOD:写操作要有明确的 capability 检查
register_rest_route( 'myplugin/v1', '/settings', array(
'methods' => 'POST',
'callback' => 'myplugin_save_settings',
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
'args' => array(
'value' => array(
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => function ( $param ) {
return is_string( $param ) && strlen( $param ) <= 200;
},
),
),
) );
// ❌ CRITICAL:回调里直接用原生 superglobal,绕过了 REST 的参数校验机制
function myplugin_save_settings() {
update_option( 'myplugin_setting', $_POST['value'] );
wp_send_json_success();
}
// ✅ GOOD:用 $request 取参数,回 WP_Error / rest_ensure_response
function myplugin_save_settings( WP_REST_Request $request ) {
$value = $request->get_param( 'value' );
if ( empty( $value ) ) {
return new WP_Error( 'missing_value', 'value 不能为空', array( 'status' => 400 ) );
}
update_option( 'myplugin_setting', $value );
return rest_ensure_response( array( 'saved' => true ) );
}
怎么安装
这个 skill 是 wordpress-skills 这个开源仓库(jorgerosal/wordpress-skills)打包的 18 个技能之一,安装一次,18 个技能一起到位,不用逐个装:
Claude Code:
| 方式 | 指令 | 适用场景 |
|---|---|---|
| 装进单个项目(推荐) | git submodule add https://github.com/jorgerosal/wordpress-skills.git .claude/plugins/wordpress-skills | 只想在这个项目用,团队成员 clone 项目就一起有 |
| 装到自己账号 | git clone https://github.com/jorgerosal/wordpress-skills.git ~/.claude/plugins/wordpress-skills | 所有项目都能用 |
| 只装这一个 skill | cp -r claude-skills/wp-rest-api-development ~/.claude/skills/ | 只想要 REST API 审查这一个功能,不要其他 17 个 |
装完重启 Claude Code,进到一个 WordPress 项目里跑 /wordpress-skills:wp-rest-review 验证有没有装成功(用 marketplace/submodule 方式装的话,指令前面会带插件命名空间 wordpress-skills:;用「只装这一个 skill」的方式则不带命名空间,直接 /wp-rest-review)。
Claude Desktop / claude.ai: 走 Settings → Capabilities → Skills,上传技能文件夹(把 claude-skills/wp-rest-api-development 这个文件夹打包上传,里面要包含 SKILL.md)——跟 Claude Code 的 git submodule/marketplace 安装方式不同,Desktop 端是手动上传 UI,装好之后同样能用自然语言或 slash 指令触发。
常见错误
- ❌ GET 端点用
__return_true被当成漏洞——公开可读数据故意开放是常见设计,这个 skill 只把「写操作」(POST/PUT/DELETE)用__return_true列为 CRITICAL,读操作不算 - ❌
permission_callback里同时写了current_user_can()检查被当成多余——这正是权限回调该放的位置,不是重复检查 - ❌ 内部专用(后台专用、只在管理员触发)的 API 也被要求跟公开 endpoint 一样严格分页/schema 复用——skill 建议按情境判断,不是所有 INFO 级建议都要照单全收
- ❌ 把「namespace 没带版本号」直接判定 CRITICAL——官方分级里这只是 WARNING,不影响功能,只是不利于以后升级
- 💡
wp_send_json()系列函数在 REST 回调里能跑、也不会报错,但会绕开 REST 该有的 header/状态码控制,skill 把这个列为 WARNING 而不是 CRITICAL,别混淆严重度
Sources
官方文档:
- wordpress-skills(GitHub 仓库)— https://github.com/jorgerosal/wordpress-skills
- wp-rest-api-development SKILL.md — https://github.com/jorgerosal/wordpress-skills/blob/main/claude-skills/wp-rest-api-development/SKILL.md