fix(fleet): close documentation safety gaps
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jarvis
2026-07-16 10:53:34 -05:00
parent 17aa94ca1a
commit 0db300dce5
3 changed files with 67 additions and 5 deletions

View File

@@ -16,7 +16,7 @@ mosaic fleet doctor
Start with `--dry-run`. It validates roster semantics, deterministic projections, private managed paths, exact holder ownership, and named-socket state without changing files or lifecycle state. Explicit `apply` and `reconcile` rebuild derived projections and enforce persisted roster state: enabled `running` agents may start, while stopped or disabled agents are not started. This guarantee does not extend to reboot/service activation yet; boot preservation remains an FCM-M3-002 hold.
`start`, `stop`, and `restart` are explicit one-shot exact-service actions. They do not persist a lifecycle change. Roster CRUD is the only way to change persisted desired state.
`start`, `stop`, and `restart` are explicit one-shot exact-service actions. They do not persist a lifecycle change. `update` preserves the agent's existing lifecycle, and no delivered operation changes durable lifecycle after creation.
Every command prints JSON. Observation commands report drift without mutation; `verify` exits non-zero on ownership mismatch, unmanaged sessions, or drift. A failed apply that wrote some derived projections reports `projections: "incomplete"` with bounded recovery to regenerate from the roster. A lifecycle failure after projections reports incomplete lifecycle work; it is never represented as a rollback or no-op.

View File

@@ -24,7 +24,7 @@
| IA pages | Every path named by the M0 checklist exists and is linked from `docs/fleet/README.md`. |
| Examples | `docs/fleet/examples/roster-v2.yaml` validates through production v2 compiler/shared resolver; shipped artifact dispositions validate through declared production readers. |
| Links | Deterministic local Markdown link test covers the entire fleet book and sitemap, including local heading-fragment resolution. |
| Sensitive/example safety | Validator scans every fenced fleet-book example plus the canonical roster for sensitive-looking keys, common credential formats, privileged commands, arbitrary command override, and hardcoded Tess/Ultron identities; docs consistently state value-free diagnostics. |
| Sensitive/example safety | Validator scans backtick- and tilde-fenced fleet-book examples plus the canonical roster for sensitive-looking keys, common credential formats (including Anthropic, OpenAI project, and Stripe restricted keys), path-qualified privileged commands, package-manager/root commands, arbitrary command override, and hardcoded Tess/Ultron identities; findings report only file/block and violation kind, never matched values. |
| Holds | `docs/reports/deferred/758-fleet-config-deferrals.md` records M3-002, M4-002, M5-002, compatibility, and repository-structure boundaries. |
## Documentation completion checklist

View File

@@ -135,9 +135,9 @@ function exampleSafetyViolationKinds(source: string): string[] {
const kinds = new Set<string>();
const sensitiveKey = /(?:secret|token|password|credential|MOSAIC_AGENT_COMMAND)/i;
const credentialFormat =
/(?:\bAKIA[0-9A-Z]{16}\b|\bAIza[0-9A-Za-z_-]{35}\b|\bgh[pousr]_[A-Za-z0-9]{20,}\b|\bgithub_pat_[A-Za-z0-9_]{20,}\b|\bglpat-[A-Za-z0-9_-]{20,}\b|\bnpm_[A-Za-z0-9]{20,}\b|\bsk_live_[A-Za-z0-9]{16,}\b|\bxox[baprs]-[A-Za-z0-9-]{10,}\b|\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b|\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b|-----BEGIN [A-Z ]*PRIVATE KEY-----|\b[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s/:]+:[^\s/@]+@)/;
/(?:\bAKIA[0-9A-Z]{16}\b|\bAIza[0-9A-Za-z_-]{35}\b|\bgh[pousr]_[A-Za-z0-9]{20,}\b|\bgithub_pat_[A-Za-z0-9_]{20,}\b|\bglpat-[A-Za-z0-9_-]{20,}\b|\bnpm_[A-Za-z0-9]{20,}\b|\bsk-ant-(?:api\d{2}-)?[A-Za-z0-9_-]{20,}\b|\bsk-proj-[A-Za-z0-9_-]{20,}\b|\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b|\bxox[baprs]-[A-Za-z0-9-]{10,}\b|\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b|\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b|-----BEGIN [A-Z ]*PRIVATE KEY-----|\b[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s/:]+:[^\s/@]+@)/;
const privilegedCommand =
/(?:^|[\n;&|])\s*(?:[$#>]\s*)?(?:env\s+(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)*|command\s+)*(?:(?:sudo|doas|pkexec)\s+|su\s+(?:-|--command\b|-c\b)|(?:systemctl|service|mount|umount|reboot|shutdown|poweroff|halt|chown|chmod|useradd|usermod|groupadd|visudo)\s+)/m;
/(?:^|[\n;&|])\s*(?:[$#>]\s*)?(?:(?:\/(?:[A-Za-z0-9._+-]+\/)*?)?(?:env|command)\s+(?:(?:-{1,2}[^\s]+|[A-Za-z_][A-Za-z0-9_]*=[^\s]+)\s+)*?)*(?:\/(?:[A-Za-z0-9._+-]+\/)*?)?(?:(?:sudo|doas|pkexec)\s+|su\s+(?:root\b|-|--command\b|-c\b)|(?:systemctl|service|mount|umount|reboot|shutdown|poweroff|halt|chown|chmod|chroot|useradd|usermod|groupadd|visudo|apt|apt-get|apt-cache|dpkg|yum|dnf|rpm|apk|pacman|zypper|emerge|snap|flatpak|brew|nix-env)\s+)/m;
if (sensitiveKey.test(source)) kinds.add('sensitive-key');
if (credentialFormat.test(source)) kinds.add('credential-format');
@@ -147,7 +147,35 @@ function exampleSafetyViolationKinds(source: string): string[] {
}
function fencedCodeBlocks(source: string): string[] {
return [...source.matchAll(/```[^\n]*\n(.*?)```/gs)].map((match): string => match[1] ?? '');
const blocks: string[] = [];
let fence: { readonly marker: string; readonly length: number } | undefined;
let lines: string[] = [];
for (const line of source.split('\n')) {
const openingRun = line.match(/^\s{0,3}(`{3,}|~{3,})[^\n]*$/)?.[1];
if (fence === undefined && openingRun !== undefined) {
fence = { marker: openingRun[0] ?? '', length: openingRun.length };
lines = [];
continue;
}
if (fence === undefined) continue;
const closingRun = line.match(/^\s{0,3}(`{3,}|~{3,})\s*$/)?.[1];
if (
closingRun !== undefined &&
closingRun[0] === fence.marker &&
closingRun.length >= fence.length
) {
blocks.push(lines.join('\n'));
fence = undefined;
lines = [];
continue;
}
lines.push(line);
}
if (fence !== undefined) blocks.push(lines.join('\n'));
return blocks;
}
const UNSAFE_EXAMPLE_FIXTURES = [
@@ -155,7 +183,27 @@ const UNSAFE_EXAMPLE_FIXTURES = [
expected: 'privileged-command',
source: ['cd /srv && su', 'do systemctl restart example'].join(''),
},
{ expected: 'privileged-command', source: '/usr/bin/sudo systemctl restart example' },
{ expected: 'privileged-command', source: '/usr/bin/apt-get install example' },
{
expected: 'privileged-command',
source: '/usr/bin/env -i /usr/bin/command -- /usr/bin/apt-get install example',
},
{ expected: 'privileged-command', source: 'su root' },
{ expected: 'privileged-command', source: '/usr/sbin/chroot /srv/example' },
{ expected: 'credential-format', source: ['ghp_', 'a'.repeat(36)].join('') },
{
expected: 'credential-format',
source: ['sk-ant-api03-', 'a'.repeat(80)].join(''),
},
{
expected: 'credential-format',
source: ['sk-proj-', 'a'.repeat(80)].join(''),
},
{
expected: 'credential-format',
source: ['rk_live_', 'a'.repeat(24)].join(''),
},
{
expected: 'credential-format',
source: ['eyJ', 'a'.repeat(12), '.', 'b'.repeat(12), '.', 'c'.repeat(12)].join(''),
@@ -198,6 +246,20 @@ describe('documentation validation regressions', (): void => {
expect(exampleSafetyViolationKinds(source)).toContain(expected);
},
);
it('extracts tilde-fenced and unclosed examples without exposing their contents', (): void => {
const sensitiveFixture = ['sk-ant-api03-', 'a'.repeat(80)].join('');
const blocks = fencedCodeBlocks(
`~~~sh\n${sensitiveFixture}\n~~~\n\n\`\`\`sh\n${sensitiveFixture}`,
);
expect(blocks).toHaveLength(2);
for (const block of blocks) {
const kinds = exampleSafetyViolationKinds(block);
expect(kinds).toContain('credential-format');
expect(JSON.stringify(kinds)).not.toContain(sensitiveFixture);
}
});
});
describe('fleet operator documentation', (): void => {