API测试
- 作者仓库星标 190,957
- 作者更新于 实时读取
- 作者仓库 n8n
- 领域
- 写作
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @n8n-io · 未声明 license
- Token 消耗评级
- 低消耗
- 接入复杂程度
- 需简单配置
- 是否需要外部 API Key
- 不需要
- 兼容的系统
- 未声明(默认跨平台)
- 底层运行要求
- 无特殊要求
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- Shell 执行
- 网络行为
- 允许外网请求
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: n8n:protect-endpoints
description: Applies n8n's RBAC scope decorators to REST endpoints. Use when creating a new @RestController…
category: 写作
runtime: 无特殊运行时
---
# n8n:protect-endpoints 输出预览
## PART A: 任务判断
- 适用问题:文章、文案、发言稿、润色或结构化表达。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“Decision / Apply the decorator / When the scope doesn't exist yet”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于文章、文案、发言稿、润色或结构化表达,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“Decision / Apply the decorator / When the scope doesn't exist yet”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、执行终端命令、会按任务需要访问外部网络、通常不需要额外 API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件、执行终端命令;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文没有稳定的斜杠命令要求。安装验证后通常全局生效,直接在对话里点名这个 Skill 并描述任务即可。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件、执行终端命令。
先用一个小任务确认它会围绕“Decision / Apply the decorator / When the scope doesn't exist yet”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: n8n:protect-endpoints
description: Applies n8n's RBAC scope decorators to REST endpoints. Use when creating a new @RestController…
category: 写作
source: n8n-io/n8n
---
# n8n:protect-endpoints
## 什么时候使用
- 用于审阅代码、文档或方案并给出可执行反馈 适合处理文章、文案、润色、翻译、总结和结构化表达,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查、可继续迭代的步骤;通常不需要额外…
- 面向文章、文案、发言稿、润色或结构化表达,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「Decision / Apply the decorator / When the scope doesn't exist yet」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件、执行终端命令;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "n8n:protect-endpoints" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> Decision / Apply the decorator / When the scope doesn't exist yet
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> 无特殊运行时 | 读取文件、写入/修改文件、执行终端命令 | 会按任务需要访问外部网络
安全层 -> 通常不需要额外 API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} Protect REST endpoints with RBAC
Rule: every authenticated route on a @RestController MUST carry an access-scope decorator. If you add a route without one, the IDOR/permission bypass is on you.
Decision
URL has :projectId → @ProjectScope('<resource>:<op>')
URL has no project → @GlobalScope('<resource>:<op>')
skipAuth: true → no decorator + comment explaining alternate auth
@ProjectScope succeeds if the user has the scope globally OR in the project named in the URL. @GlobalScope ignores project relations entirely.
Both decorators come from @n8n/decorators. The middleware lives in packages/cli/src/controller.registry.ts (createScopedMiddleware) and resolves access via userHasScopes in packages/cli/src/permissions.ee/check-access.ts.
Apply the decorator
import { Get, Post, ProjectScope, RestController } from '@n8n/decorators';
@RestController('/projects/:projectId/widgets')
export class WidgetsController {
@Post('/')
@ProjectScope('widget:create') // create
async create(...) { ... }
@Get('/:widgetId')
@ProjectScope('widget:read') // read one
async get(...) { ... }
@Get('/')
@ProjectScope('widget:list') // list
async list(...) { ... }
@Patch('/:widgetId')
@ProjectScope('widget:update') // update
async update(...) { ... }
@Delete('/:widgetId')
@ProjectScope('widget:delete') // delete
async delete(...) { ... }
}
Conventions:
- One decorator per route, placed directly under the HTTP-method decorator.
- Use the most specific scope that fits. Reuse
*:updatefor state-changing actions likepublish/unpublish/buildunless the resource needs to gate them separately (seeworkflow:publishfor the precedent). - Routes without
:projectIdand not global-only operations are usually a design smell — flag it.
When the scope doesn't exist yet
Add the resource and ops in packages/@n8n/permissions/:
src/constants.ee.ts— add toRESOURCES(alphabetical):
Thewidget: [...DEFAULT_OPERATIONS, 'execute'] as const,Scopeunion (<resource>:<op>template-literal type) auto-derives.src/scope-information.ts— add a display name + description per scope.src/roles/scopes/project-scopes.ee.ts— add to project roles. Match theworkflowprecedent unless product says otherwise:REGULAR_PROJECT_ADMIN_SCOPES,PERSONAL_PROJECT_OWNER_SCOPES,PROJECT_EDITOR_SCOPES→ all CRUDL+execute scopes.PROJECT_VIEWER_SCOPES→ read/list/execute only.PROJECT_CHAT_USER_SCOPES→ execute only (if applicable).
src/roles/scopes/global-scopes.ee.ts— add toGLOBAL_OWNER_SCOPES(admin inherits viaconcat()). Do not add to member/chat-user globals — they get scopes via project relations.- Personal-space publishing: if you add a
<resource>:publishscope, also append it toPERSONAL_SPACE_PUBLISHING_SETTING.scopesinconstants.ee.tsso personal-owner gating matchesworkflow:publish. - Frontend wiring — three files in the editor; skipping any of them means the new scopes will not appear in the project-role configuration UI:
packages/frontend/editor-ui/src/app/stores/rbac.store.ts— add<resource>: {}toscopesByResourceId(typecheck will fail otherwise).packages/frontend/editor-ui/src/features/project-roles/projectRoleScopes.ts— add the resource toUI_OPERATIONS(operations to render in the permissions matrix, in display order) and toSCOPE_TYPES(the order the resource group appears on the page).packages/frontend/@n8n/i18n/src/locales/en.json— addprojectRoles.<resource>:<op>(column label) andprojectRoles.<resource>:<op>.tooltip(hover description) for every op, plusprojectRoles.type.<resource>(the group header).
- Snapshot — update
packages/@n8n/permissions/src/__tests__/__snapshots__/scope-information.test.ts.snapto include the new<resource>:*entries.
No DB migration needed — AuthRolesService.init() syncs scopes/roles on every startup. Custom team roles created in the UI are not auto-updated; mention this in the PR description.
Public / unauthenticated routes
{ skipAuth: true } skips the auth middleware → req.user is undefined → adding @ProjectScope would 401 every call. Public routes (third-party webhooks, signed callbacks) must:
- Omit the scope decorator.
- Authenticate via signature/HMAC verification inside the handler (or another route-specific mechanism).
- Carry a comment explaining why no scope is applied, so the next reviewer doesn't try to "fix" it.
Example:
// Third-party webhook callback: do not add @ProjectScope. Auth happens
// via per-platform signature verification inside webhookHandler, and
// :projectId is unused in the (agentId, platform) lookup.
@Post('/:agentId/webhooks/:platform', { skipAuth: true, allowBots: true })
async handleWebhook(...) { ... }
Verify with a route-metadata test
Add a regression test that fails when a future route is added without a scope. Iterate every route on the controller via ControllerRegistryMetadata and assert the gate.
import { ControllerRegistryMetadata } from '@n8n/decorators';
import { Container } from '@n8n/di';
import { WidgetsController } from '../widgets.controller';
const UNAUTHENTICATED_HANDLERS = new Set<string>(); // add public handler names here
const metadata = Container.get(ControllerRegistryMetadata).getControllerMetadata(
WidgetsController as never,
);
const routeCases = Array.from(metadata.routes.entries()).map(([handlerName, route]) => ({
handlerName, route,
}));
describe('WidgetsController route access scopes', () => {
it.each(routeCases)(
'$handlerName is gated by a project-scoped widget:* check',
({ handlerName, route }) => {
if (UNAUTHENTICATED_HANDLERS.has(handlerName)) {
expect(route.accessScope).toBeUndefined();
expect(route.skipAuth).toBe(true);
return;
}
expect(route.accessScope).toBeDefined();
expect(route.accessScope?.globalOnly).toBe(false);
expect(route.accessScope?.scope.startsWith('widget:')).toBe(true);
},
);
});
Defense in depth (still required)
Decorator alone is not enough when handlers leak data via downstream calls. Service/repository methods should still filter by projectId (or user-scoped helpers like findByUser). The decorator gates who can call this URL; the service gates what they can read. Both, always.
Reference patterns
- Project-scoped CRUD:
packages/cli/src/workflows/workflows.controller.ts,packages/cli/src/credentials/credentials.controller.ts,packages/cli/src/modules/data-table/data-table.controller.ts. - Mixed global + project:
packages/cli/src/controllers/project.controller.ts.
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核