mirror of
https://github.com/anthropics/claude-code.git
synced 2026-09-29 16:59:25 +08:00
sec-default: collector records continue past the user tier
This commit is contained in:
@@ -26,6 +26,7 @@ settings it decides by.
|
||||
| `classic.*` | Continue past the user tier: the organization's settings hooks see the engine's input and their answer stands. |
|
||||
| `prompt.section`, `prompt.context`, `skill.prompt`, `attribution.text` | Continue past the user tier: managed CLAUDE.md, rules and policy skills reach the model as written. A person's plugins keep `prompt.submit` and its additive context. |
|
||||
| `settings.read` | Continue past the user tier: no user hook rewrites what any caller reads as settings, this plugin's own policy reads included. |
|
||||
| `telemetry.log` `{ to: "collector" }` | Continue past the user tier: no plugin a person installed drops or rewrites a record on its way to the collector the organization configured. The organization's own plugins, in `prepend` and `append`, still may. |
|
||||
| `tool.describe`, `command.describe`, `agent.offer`, `agent.spawn` | When the subject's pinned `e.provider.tier` is `prepend` or `append` (a policy-installed plugin, the managed folder, a policy MCP server), continue past the user tier; a subject provided by `user`, `builtin` or `core` passes. |
|
||||
| `tool.register` | A caller in `prepend` or `append` continues past the user tier. A `user`-tier caller is refused by name while managed settings hold `allowedMcpServers` (set at all, empty included); otherwise it passes. |
|
||||
| `tool.list` | The tools of the organization's managed MCP servers are listed as the organization's tiers listed them; every other tool as the user tier left it. With no policy to read, or a refusal from either listing, the organization's listing stands whole. |
|
||||
@@ -34,8 +35,9 @@ settings it decides by.
|
||||
## What it hooks
|
||||
|
||||
`classic.*`, `prompt.section`, `prompt.context`, `skill.prompt`,
|
||||
`attribution.text`, `settings.read`, `tool.describe`, `command.describe`,
|
||||
`agent.offer`, `agent.spawn`, `tool.register`, `tool.list`.
|
||||
`attribution.text`, `settings.read`, `telemetry.log` (the collector's
|
||||
stream), `tool.describe`, `command.describe`, `agent.offer`, `agent.spawn`,
|
||||
`tool.register`, `tool.list`.
|
||||
|
||||
## What it calls on `$`
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ export function register(on: On) {
|
||||
|
||||
on('settings.read', ($, e, next) => next.to(e, 'append'))
|
||||
|
||||
on('telemetry.log', { to: 'collector' }, ($, e, next) =>
|
||||
next.to(e, 'append'),
|
||||
)
|
||||
|
||||
on('tool.describe', ($, e, next) => pastUsers(e, next))
|
||||
on('command.describe', ($, e, next) => pastUsers(e, next))
|
||||
on('agent.offer', ($, e, next) => pastUsers(e, next))
|
||||
|
||||
+1
@@ -28,5 +28,6 @@ export * from './tools.js'
|
||||
export * from './tools-command.js'
|
||||
export * from './tools-registered.js'
|
||||
export * from './user-reachable-providers.js'
|
||||
export * from './withholding.js'
|
||||
|
||||
export * as default from '.'
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import type { Plugin } from 'claude-code/testing'
|
||||
|
||||
/**
|
||||
* A plugin the person installed that keeps the collector record named
|
||||
* `withheld` from the collector and marks every other one `edited`.
|
||||
*/
|
||||
export const withholding: Plugin = {
|
||||
name: 'withholding',
|
||||
register(on) {
|
||||
on('telemetry.log', { to: 'collector' }, ($, e, next) =>
|
||||
e.to === 'collector' && e.event !== 'withheld'
|
||||
? next({ ...e, attributes: { ...e.attributes, edited: true } })
|
||||
: { deny: 'kept from the collector' },
|
||||
)
|
||||
},
|
||||
}
|
||||
@@ -137,6 +137,37 @@ describe('register', () => {
|
||||
},
|
||||
)
|
||||
|
||||
test(
|
||||
'a collector record passes over the plugins the person installed',
|
||||
{ plugins: [Fixtures.withholding] },
|
||||
async ($, on) => {
|
||||
const reached: unknown[] = []
|
||||
on('telemetry.log', { to: 'collector' }, ($, e) => {
|
||||
reached.push([e.event, e.to === 'collector' && e.attributes.edited])
|
||||
|
||||
return { value: undefined }
|
||||
})
|
||||
|
||||
await $.telemetry.log({
|
||||
to: 'collector',
|
||||
event: 'kept',
|
||||
attributes: {},
|
||||
loggedAt: '2026-09-26T10:00:00.000Z',
|
||||
})
|
||||
await $.telemetry.log({
|
||||
to: 'collector',
|
||||
event: 'withheld',
|
||||
attributes: {},
|
||||
loggedAt: '2026-09-26T10:00:01.000Z',
|
||||
})
|
||||
|
||||
expect(reached).toEqual([
|
||||
['kept', undefined],
|
||||
['withheld', undefined],
|
||||
])
|
||||
},
|
||||
)
|
||||
|
||||
test(
|
||||
"a user plugin's rewrite of policy is skipped for every other reader",
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user