@guilz-dev/belay 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -1
- package/dist/adapters/codex/hooks.d.ts +1 -0
- package/dist/adapters/codex/hooks.js +3 -0
- package/dist/adapters/cursor/dispatcher-generation.d.ts +3 -0
- package/dist/adapters/cursor/dispatcher-generation.js +5 -0
- package/dist/adapters/cursor/hook-dispatch-entry.d.ts +7 -0
- package/dist/adapters/cursor/hook-dispatch-entry.js +126 -0
- package/dist/adapters/cursor/hook-router.d.ts +23 -0
- package/dist/adapters/cursor/hook-router.js +266 -0
- package/dist/adapters/cursor/hooks.d.ts +4 -0
- package/dist/adapters/cursor/hooks.js +99 -8
- package/dist/adapters/cursor/routing-layout.d.ts +5 -0
- package/dist/adapters/cursor/routing-layout.js +29 -0
- package/dist/adapters/cursor/runtime-entry.d.ts +17 -0
- package/dist/adapters/cursor/runtime-entry.js +39 -39
- package/dist/adapters/layouts/scope.d.ts +5 -0
- package/dist/adapters/layouts/scope.js +71 -0
- package/dist/adapters/shared/gate-runtime.js +1 -0
- package/dist/bundle/claude-runtime.mjs +1932 -1740
- package/dist/bundle/codex-runtime.mjs +1934 -1742
- package/dist/bundle/cursor-dispatcher.mjs +443 -0
- package/dist/bundle/cursor-runtime.mjs +2017 -1821
- package/dist/commands/doctor.js +191 -15
- package/dist/commands/health-snapshot.js +9 -4
- package/dist/commands/recovery-checkpoints.d.ts +3 -3
- package/dist/config-io.js +11 -2
- package/dist/core/audit-legacy-archive.js +43 -25
- package/dist/core/classify-tool.js +16 -8
- package/dist/core/effect-ir/argv-delegate.d.ts +9 -0
- package/dist/core/effect-ir/argv-delegate.js +42 -0
- package/dist/core/effect-ir/shell-lower/argv-delegate-gate.d.ts +3 -0
- package/dist/core/effect-ir/shell-lower/argv-delegate-gate.js +40 -0
- package/dist/core/effect-ir/shell-lower/augment.d.ts +4 -0
- package/dist/core/effect-ir/shell-lower/augment.js +97 -0
- package/dist/core/effect-ir/shell-lower/context.d.ts +10 -0
- package/dist/core/effect-ir/shell-lower/context.js +1 -0
- package/dist/core/effect-ir/shell-lower/decode-process.d.ts +9 -0
- package/dist/core/effect-ir/shell-lower/decode-process.js +201 -0
- package/dist/core/effect-ir/shell-lower/decoders/belay.d.ts +2 -0
- package/dist/core/effect-ir/shell-lower/decoders/belay.js +29 -0
- package/dist/core/effect-ir/shell-lower/decoders/builtins.d.ts +7 -0
- package/dist/core/effect-ir/shell-lower/decoders/builtins.js +194 -0
- package/dist/core/effect-ir/shell-lower/decoders/decode-package-exec.d.ts +4 -0
- package/dist/core/effect-ir/shell-lower/decoders/decode-package-exec.js +68 -0
- package/dist/core/effect-ir/shell-lower/decoders/docker.d.ts +3 -0
- package/dist/core/effect-ir/shell-lower/decoders/docker.js +43 -0
- package/dist/core/effect-ir/shell-lower/decoders/filesystem.d.ts +9 -0
- package/dist/core/effect-ir/shell-lower/decoders/filesystem.js +217 -0
- package/dist/core/effect-ir/shell-lower/decoders/prisma.d.ts +2 -0
- package/dist/core/effect-ir/shell-lower/decoders/prisma.js +54 -0
- package/dist/core/effect-ir/shell-lower/decoders/ruby.d.ts +11 -0
- package/dist/core/effect-ir/shell-lower/decoders/ruby.js +147 -0
- package/dist/core/effect-ir/shell-lower/decoders/toolchain.d.ts +7 -0
- package/dist/core/effect-ir/shell-lower/decoders/toolchain.js +155 -0
- package/dist/core/effect-ir/shell-lower/requirement.d.ts +12 -0
- package/dist/core/effect-ir/shell-lower/requirement.js +65 -0
- package/dist/core/effect-ir/shell-lower/segment.d.ts +11 -0
- package/dist/core/effect-ir/shell-lower/segment.js +82 -0
- package/dist/core/effect-ir/shell-lower/tokens.d.ts +26 -0
- package/dist/core/effect-ir/shell-lower/tokens.js +183 -0
- package/dist/core/effect-ir/shell-lower.d.ts +2 -7
- package/dist/core/effect-ir/shell-lower.js +60 -1502
- package/dist/core/glob.js +32 -16
- package/dist/core/integrity.d.ts +2 -2
- package/dist/core/integrity.js +52 -12
- package/dist/core/replay-scrub.d.ts +2 -0
- package/dist/core/replay-scrub.js +8 -0
- package/dist/core/shell-substitution.js +2 -2
- package/dist/core/shell-unparseable.js +3 -1
- package/dist/defaults.d.ts +7 -3
- package/dist/defaults.js +29 -22
- package/dist/installer/bootstrap.d.ts +1 -0
- package/dist/installer/bootstrap.js +2 -2
- package/dist/installer/runtime-artifacts.js +43 -13
- package/dist/installer.js +44 -9
- package/dist/node-resolution.d.ts +1 -0
- package/dist/node-resolution.js +61 -0
- package/dist/templates.d.ts +7 -4
- package/dist/templates.js +42 -4
- package/dist/types.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { isFdDuplication, isRedirectOperator } from '../../shell-tokenizer.js';
|
|
2
|
+
import { addSecretRead, addWriteEffects, requirement } from './requirement.js';
|
|
3
|
+
import { expandKnownVariables, resolvePathOperand } from './tokens.js';
|
|
4
|
+
export function addRedirectEffects(requirements, tokens, env, context, segment) {
|
|
5
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
6
|
+
const operator = tokens[index] ?? '';
|
|
7
|
+
if (!isRedirectOperator(operator) || isFdDuplication(operator)) {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const rawTarget = tokens[index + 1];
|
|
11
|
+
if (!rawTarget) {
|
|
12
|
+
requirements.push(requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
|
|
13
|
+
'shell.redirect_target_missing',
|
|
14
|
+
]));
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (operator.includes('<<')) {
|
|
18
|
+
requirements.push(requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
|
|
19
|
+
'shell.heredoc_opaque',
|
|
20
|
+
]));
|
|
21
|
+
index += 1;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const target = expandKnownVariables(rawTarget, env);
|
|
25
|
+
const resolved = resolvePathOperand(target, context.cwd);
|
|
26
|
+
if (resolved === '/dev/null') {
|
|
27
|
+
index += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (operator.includes('<')) {
|
|
31
|
+
requirements.push(requirement('fs.read', 'fs.read', { kind: 'path', path: resolved }, segment, [
|
|
32
|
+
'shell.input_redirect',
|
|
33
|
+
]));
|
|
34
|
+
addSecretRead(requirements, resolved, segment);
|
|
35
|
+
}
|
|
36
|
+
if (operator.includes('>')) {
|
|
37
|
+
addWriteEffects(requirements, resolved, segment, ['shell.output_redirect']);
|
|
38
|
+
}
|
|
39
|
+
index += 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function effectChangingEnvironmentSignals(head, env, changedNames) {
|
|
43
|
+
if (head === 'curl' &&
|
|
44
|
+
(env.CURL_HOME ||
|
|
45
|
+
changedNames.has('CURL_HOME') ||
|
|
46
|
+
changedNames.has('HOME') ||
|
|
47
|
+
changedNames.has('XDG_CONFIG_HOME'))) {
|
|
48
|
+
return ['egress.curl.environment_config_override'];
|
|
49
|
+
}
|
|
50
|
+
if (head === 'wget' && (env.WGETRC || changedNames.has('WGETRC') || changedNames.has('HOME'))) {
|
|
51
|
+
return ['egress.wget.environment_config_override'];
|
|
52
|
+
}
|
|
53
|
+
if (head !== 'git') {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const hazardousNames = new Set([
|
|
57
|
+
'GIT_ASKPASS',
|
|
58
|
+
'GIT_ALTERNATE_OBJECT_DIRECTORIES',
|
|
59
|
+
'GIT_CEILING_DIRECTORIES',
|
|
60
|
+
'GIT_COMMON_DIR',
|
|
61
|
+
'GIT_CONFIG',
|
|
62
|
+
'GIT_CONFIG_GLOBAL',
|
|
63
|
+
'GIT_CONFIG_SYSTEM',
|
|
64
|
+
'GIT_DIR',
|
|
65
|
+
'GIT_DISCOVERY_ACROSS_FILESYSTEM',
|
|
66
|
+
'GIT_EDITOR',
|
|
67
|
+
'GIT_EXEC_PATH',
|
|
68
|
+
'GIT_EXTERNAL_DIFF',
|
|
69
|
+
'GIT_INDEX_FILE',
|
|
70
|
+
'GIT_NAMESPACE',
|
|
71
|
+
'GIT_OBJECT_DIRECTORY',
|
|
72
|
+
'GIT_PAGER',
|
|
73
|
+
'GIT_PROXY_COMMAND',
|
|
74
|
+
'GIT_QUARANTINE_PATH',
|
|
75
|
+
'GIT_SEQUENCE_EDITOR',
|
|
76
|
+
'GIT_SHALLOW_FILE',
|
|
77
|
+
'GIT_SSH',
|
|
78
|
+
'GIT_SSH_COMMAND',
|
|
79
|
+
'GIT_TEMPLATE_DIR',
|
|
80
|
+
'GIT_WORK_TREE',
|
|
81
|
+
'SSH_ASKPASS',
|
|
82
|
+
]);
|
|
83
|
+
const overridden = changedNames.has('HOME') ||
|
|
84
|
+
changedNames.has('XDG_CONFIG_HOME') ||
|
|
85
|
+
Object.entries(env).some(([name, value]) => {
|
|
86
|
+
if (!value) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (hazardousNames.has(name) ||
|
|
90
|
+
/^GIT_CONFIG_(?:KEY|VALUE)_\d+$/.test(name) ||
|
|
91
|
+
/^GIT_TRACE/.test(name)) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return name === 'GIT_CONFIG_COUNT' && value !== '0';
|
|
95
|
+
});
|
|
96
|
+
return overridden ? ['git.environment_execution_or_config_override'] : [];
|
|
97
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface LowerShellEffectPlanParams {
|
|
2
|
+
command: string;
|
|
3
|
+
cwd: string;
|
|
4
|
+
repoRoot: string;
|
|
5
|
+
inputFingerprint: string;
|
|
6
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
7
|
+
}
|
|
8
|
+
export interface LowerContext extends LowerShellEffectPlanParams {
|
|
9
|
+
depth: number;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ShellEffectRequirement } from '../shell-build.js';
|
|
2
|
+
export declare function decodeProcessOrFilesystem(params: {
|
|
3
|
+
tokens: string[];
|
|
4
|
+
head: string;
|
|
5
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
6
|
+
cwd: string;
|
|
7
|
+
repoRoot: string;
|
|
8
|
+
segment: string;
|
|
9
|
+
}): ShellEffectRequirement[];
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { isCommandInspection } from '../../verdict/parser.js';
|
|
3
|
+
import { decodeBelay } from './decoders/belay.js';
|
|
4
|
+
import { bashSyntaxTarget, decodeShellControlBuiltin, validFilesystemTest, validLsof, validPs, } from './decoders/builtins.js';
|
|
5
|
+
import { decodeDockerComposeRun, validDockerInfo } from './decoders/docker.js';
|
|
6
|
+
import { decodeCopyMove, decodeRm, filesystemReadOperands, filesystemWriteOperands, } from './decoders/filesystem.js';
|
|
7
|
+
import { decodePrisma } from './decoders/prisma.js';
|
|
8
|
+
import { decodeBundleExecInner, decodeRuby, decodeRuntimeMetadataProcess } from './decoders/ruby.js';
|
|
9
|
+
import { decodeGo, decodeNode, decodeRsync, decodeSed, decodeTsc } from './decoders/toolchain.js';
|
|
10
|
+
import { addSecretRead, addWriteEffects, processRequirement, requirement, unsupportedProcess, } from './requirement.js';
|
|
11
|
+
import { ENV_PREFIX_PATTERN, isMetadataOnlyArgv, isShellHead, resolvePathOperand, } from './tokens.js';
|
|
12
|
+
export function decodeProcessOrFilesystem(params) {
|
|
13
|
+
const { tokens, head, env, cwd, repoRoot, segment } = params;
|
|
14
|
+
const args = tokens.slice(1);
|
|
15
|
+
if (tokens.length > 0 && tokens.every((token) => ENV_PREFIX_PATTERN.test(token))) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const shellControl = decodeShellControlBuiltin(head, args);
|
|
19
|
+
if (shellControl) {
|
|
20
|
+
return shellControl;
|
|
21
|
+
}
|
|
22
|
+
if (isCommandInspection(tokens)) {
|
|
23
|
+
return [processRequirement(head, 'inspect', segment, ['process.inspect.command_lookup'])];
|
|
24
|
+
}
|
|
25
|
+
if (head === 'lsof') {
|
|
26
|
+
return validLsof(args)
|
|
27
|
+
? [processRequirement(head, 'inspect', segment, ['process.inspect.lsof'])]
|
|
28
|
+
: unsupportedProcess(head, segment, 'process.lsof_grammar_incomplete');
|
|
29
|
+
}
|
|
30
|
+
if (head === 'ps') {
|
|
31
|
+
return validPs(args)
|
|
32
|
+
? [processRequirement(head, 'inspect', segment, ['process.inspect.ps'])]
|
|
33
|
+
: unsupportedProcess(head, segment, 'process.ps_grammar_incomplete');
|
|
34
|
+
}
|
|
35
|
+
if (head === 'test' || head === '[') {
|
|
36
|
+
return validFilesystemTest(head, args)
|
|
37
|
+
? [processRequirement(head, 'inspect', segment, ['process.inspect.filesystem_test'])]
|
|
38
|
+
: unsupportedProcess(head, segment, 'process.filesystem_test_grammar_incomplete');
|
|
39
|
+
}
|
|
40
|
+
if (head === 'docker') {
|
|
41
|
+
if (args[0] === 'info') {
|
|
42
|
+
return validDockerInfo(args.slice(1))
|
|
43
|
+
? [processRequirement(head, 'inspect', segment, ['process.inspect.docker_info'])]
|
|
44
|
+
: unsupportedProcess(head, segment, 'process.docker_info_grammar_incomplete');
|
|
45
|
+
}
|
|
46
|
+
if (args[0] === 'compose' &&
|
|
47
|
+
['up', 'down', 'start', 'stop', 'restart', 'create', 'rm'].includes(args[1] ?? '')) {
|
|
48
|
+
return [processRequirement(head, 'spawn', segment, ['service.local_mutation'])];
|
|
49
|
+
}
|
|
50
|
+
if (args[0] === 'push' ||
|
|
51
|
+
(args[0] === 'build' && args.includes('--push')) ||
|
|
52
|
+
(args[0] === 'buildx' &&
|
|
53
|
+
args[1] === 'build' &&
|
|
54
|
+
(args.includes('--push') ||
|
|
55
|
+
args.some((arg) => arg.startsWith('--output=type=registry') || arg.startsWith('-o=type=registry'))))) {
|
|
56
|
+
return [
|
|
57
|
+
processRequirement(head, 'spawn', segment, ['tier0_external', 'docker.remote_mutation']),
|
|
58
|
+
requirement('network.connect', 'network.connect', {
|
|
59
|
+
kind: 'network',
|
|
60
|
+
host: 'registry',
|
|
61
|
+
protocol: 'container-registry',
|
|
62
|
+
mode: 'mutate',
|
|
63
|
+
payload: 'present',
|
|
64
|
+
}, segment, ['tier0_external', 'docker.remote_mutation']),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
if (args[0] === 'run' ||
|
|
68
|
+
args[0] === 'create' ||
|
|
69
|
+
args[0] === 'pull' ||
|
|
70
|
+
args[0] === 'build' ||
|
|
71
|
+
(args[0] === 'buildx' && args[1] === 'build')) {
|
|
72
|
+
return [
|
|
73
|
+
processRequirement(head, 'spawn', segment, ['process.docker_spawn']),
|
|
74
|
+
requirement('network.acquire', 'network.connect', {
|
|
75
|
+
kind: 'network',
|
|
76
|
+
host: 'unknown',
|
|
77
|
+
protocol: 'container-registry',
|
|
78
|
+
mode: 'ambiguous',
|
|
79
|
+
payload: 'none',
|
|
80
|
+
}, segment, ['docker.image_acquisition_possible']),
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
return [processRequirement(head, 'spawn', segment, ['process.docker_spawn'])];
|
|
84
|
+
}
|
|
85
|
+
if (isShellHead(head)) {
|
|
86
|
+
const syntax = bashSyntaxTarget(args);
|
|
87
|
+
if (syntax) {
|
|
88
|
+
return [
|
|
89
|
+
processRequirement(head, 'inspect', segment, ['process.inspect.shell_syntax']),
|
|
90
|
+
requirement('fs.read', 'fs.read', { kind: 'path', path: path.resolve(cwd, syntax) }, segment, ['shell.syntax_source_read']),
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
return unsupportedProcess(head, segment, 'process.shell_grammar_incomplete');
|
|
94
|
+
}
|
|
95
|
+
if (head === 'prisma') {
|
|
96
|
+
return decodePrisma(args, env, repoRoot, segment);
|
|
97
|
+
}
|
|
98
|
+
const runtimeMetadata = decodeRuntimeMetadataProcess(head, args, segment);
|
|
99
|
+
if (runtimeMetadata) {
|
|
100
|
+
return runtimeMetadata;
|
|
101
|
+
}
|
|
102
|
+
if (head === 'ruby') {
|
|
103
|
+
return decodeRuby(args, cwd, repoRoot, segment);
|
|
104
|
+
}
|
|
105
|
+
if (head === 'rubocop' || head === 'rspec') {
|
|
106
|
+
const decoded = decodeBundleExecInner(head, args, segment);
|
|
107
|
+
if (decoded) {
|
|
108
|
+
return decoded;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const dockerCompose = decodeDockerComposeRun(head, args, segment);
|
|
112
|
+
if (dockerCompose) {
|
|
113
|
+
return dockerCompose;
|
|
114
|
+
}
|
|
115
|
+
if ((head === 'npm' || head === 'pnpm') && args.length === 1 && isMetadataOnlyArgv(args)) {
|
|
116
|
+
return [processRequirement(head, 'inspect', segment, ['process.inspect.package_manager'])];
|
|
117
|
+
}
|
|
118
|
+
if (head === 'belay') {
|
|
119
|
+
return decodeBelay(args, repoRoot, segment);
|
|
120
|
+
}
|
|
121
|
+
if (head === 'tsc') {
|
|
122
|
+
return decodeTsc(args, cwd, segment);
|
|
123
|
+
}
|
|
124
|
+
if (head === 'go') {
|
|
125
|
+
return decodeGo(args, segment);
|
|
126
|
+
}
|
|
127
|
+
if (head === 'rsync') {
|
|
128
|
+
return decodeRsync(args, cwd, segment);
|
|
129
|
+
}
|
|
130
|
+
if (head === 'sed') {
|
|
131
|
+
return decodeSed(args, cwd, segment);
|
|
132
|
+
}
|
|
133
|
+
if (head === 'base64' && args.length === 1 && ['-d', '-D', '--decode'].includes(args[0] ?? '')) {
|
|
134
|
+
return [processRequirement(head, 'inspect', segment, ['process.inspect.base64_stdin'])];
|
|
135
|
+
}
|
|
136
|
+
if (head === 'node') {
|
|
137
|
+
return decodeNode(args, cwd, segment);
|
|
138
|
+
}
|
|
139
|
+
if (head === 'vite' || head === 'vite-node') {
|
|
140
|
+
return [processRequirement(head, 'spawn', segment, ['process.local_dev_spawn'])];
|
|
141
|
+
}
|
|
142
|
+
if (((head === 'npm' || head === 'pnpm') && args[0] === 'publish') ||
|
|
143
|
+
(head === 'terraform' && args[0] === 'apply')) {
|
|
144
|
+
return [
|
|
145
|
+
processRequirement(head, 'spawn', segment, ['tier0_external']),
|
|
146
|
+
requirement('network.connect', 'network.connect', {
|
|
147
|
+
kind: 'network',
|
|
148
|
+
host: 'control-plane',
|
|
149
|
+
protocol: head,
|
|
150
|
+
mode: 'mutate',
|
|
151
|
+
payload: 'present',
|
|
152
|
+
}, segment, ['tier0_external']),
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
if (head === 'cd') {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
if (head === 'cp' || head === 'mv') {
|
|
159
|
+
return decodeCopyMove(head, args, cwd, segment);
|
|
160
|
+
}
|
|
161
|
+
if (head === 'rm') {
|
|
162
|
+
return decodeRm(args, cwd, repoRoot, segment);
|
|
163
|
+
}
|
|
164
|
+
if (head === 'vitest' || head === 'eslint' || head === 'biome' || head === 'belay') {
|
|
165
|
+
return [processRequirement(head, 'spawn', segment, ['process.known_local_spawn'])];
|
|
166
|
+
}
|
|
167
|
+
if (head === 'pwd' || head === 'which' || head === 'whoami') {
|
|
168
|
+
return [processRequirement(head, 'inspect', segment, ['process.pure_inspection'])];
|
|
169
|
+
}
|
|
170
|
+
const readOperands = filesystemReadOperands(head, args);
|
|
171
|
+
if (readOperands !== null) {
|
|
172
|
+
const lowered = [processRequirement(head, 'inspect', segment, ['process.filesystem_inspect'])];
|
|
173
|
+
for (const operand of readOperands) {
|
|
174
|
+
const resolved = resolvePathOperand(operand, cwd);
|
|
175
|
+
lowered.push(requirement('fs.read', 'fs.read', { kind: 'path', path: resolved }, segment, [
|
|
176
|
+
'filesystem.read',
|
|
177
|
+
]));
|
|
178
|
+
addSecretRead(lowered, resolved, segment);
|
|
179
|
+
}
|
|
180
|
+
return lowered;
|
|
181
|
+
}
|
|
182
|
+
const writeOperands = filesystemWriteOperands(head, args);
|
|
183
|
+
if (writeOperands !== null) {
|
|
184
|
+
const lowered = [processRequirement(head, 'spawn', segment, ['process.filesystem_mutation'])];
|
|
185
|
+
for (const operand of writeOperands) {
|
|
186
|
+
addWriteEffects(lowered, resolvePathOperand(operand, cwd), segment, ['filesystem.write']);
|
|
187
|
+
}
|
|
188
|
+
return lowered;
|
|
189
|
+
}
|
|
190
|
+
if (head === 'printf' || head === 'echo' || head === 'true' || head === 'false' || head === ':') {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
if (!head) {
|
|
194
|
+
return [
|
|
195
|
+
requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
|
|
196
|
+
'shell.segment_empty',
|
|
197
|
+
]),
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
return unsupportedProcess(head, segment, 'process.grammar_unknown');
|
|
201
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { processRequirement, requirement } from '../requirement.js';
|
|
3
|
+
export function decodeBelay(args, repoRoot, segment) {
|
|
4
|
+
const [section, operation, key] = args;
|
|
5
|
+
const judgeCommand = section === 'judge' &&
|
|
6
|
+
operation !== undefined &&
|
|
7
|
+
['consent', 'list', 'status', 'test', 'use'].includes(operation);
|
|
8
|
+
const configRead = section === 'config' &&
|
|
9
|
+
(operation === undefined ||
|
|
10
|
+
operation === 'list' ||
|
|
11
|
+
(operation === 'get' && key?.startsWith('judge.')));
|
|
12
|
+
const configJudgeMutation = section === 'config' &&
|
|
13
|
+
((['set', 'unset'].includes(operation ?? '') && key?.startsWith('judge.')) ||
|
|
14
|
+
(operation === 'credential' && key === 'mode'));
|
|
15
|
+
if (judgeCommand || configRead || configJudgeMutation) {
|
|
16
|
+
return [
|
|
17
|
+
processRequirement('belay', 'inspect', segment, [
|
|
18
|
+
'belay_control_plane_command',
|
|
19
|
+
configJudgeMutation ? 'belay.config_judge_mutation' : 'belay.config_read',
|
|
20
|
+
]),
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
if (section === 'config' && ['set', 'unset', 'credential'].includes(operation ?? '')) {
|
|
24
|
+
return [
|
|
25
|
+
requirement('control_plane.write', 'control_plane.write', { kind: 'path', path: path.join(repoRoot, '.belay-control-plane') }, segment, ['belay.config_non_judge_mutation']),
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
return [processRequirement('belay', 'spawn', segment, ['process.known_local_spawn'])];
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ShellEffectRequirement } from '../../shell-build.js';
|
|
2
|
+
export declare function decodeSetBuiltin(args: string[]): boolean;
|
|
3
|
+
export declare function decodeShellControlBuiltin(head: string, args: string[]): ShellEffectRequirement[] | null;
|
|
4
|
+
export declare function validLsof(args: string[]): boolean;
|
|
5
|
+
export declare function validPs(args: string[]): boolean;
|
|
6
|
+
export declare function validFilesystemTest(head: string, args: string[]): boolean;
|
|
7
|
+
export declare function bashSyntaxTarget(args: string[]): string | null;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { isOptionToken } from '../tokens.js';
|
|
2
|
+
export function decodeSetBuiltin(args) {
|
|
3
|
+
let index = 0;
|
|
4
|
+
while (index < args.length) {
|
|
5
|
+
const arg = args[index] ?? '';
|
|
6
|
+
if (arg === '--') {
|
|
7
|
+
index += 1;
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (arg === '-o' || arg === '+o') {
|
|
11
|
+
if (!args[index + 1]) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
index += 2;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (/^[-+][A-Za-z0-9]+$/.test(arg)) {
|
|
18
|
+
index += 1;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
export function decodeShellControlBuiltin(head, args) {
|
|
26
|
+
if (head === 'set') {
|
|
27
|
+
return decodeSetBuiltin(args) ? [] : null;
|
|
28
|
+
}
|
|
29
|
+
if (head === 'wait') {
|
|
30
|
+
if (args.length === 0 || args.every((arg) => /^\d+$/.test(arg))) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (head === 'exit') {
|
|
36
|
+
if (args.length === 0 || (args.length === 1 && /^-?\d+$/.test(args[0] ?? ''))) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
export function validLsof(args) {
|
|
44
|
+
const flagsWithoutValues = new Set(['-a', '-b', '-l', '-n', '-P', '-R', '-t', '-V']);
|
|
45
|
+
const flagsWithValues = new Set(['-c', '-d', '-F', '-g', '-p', '-T', '-u', '+d', '+D']);
|
|
46
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
47
|
+
const arg = args[index] ?? '';
|
|
48
|
+
if (flagsWithoutValues.has(arg) || arg === '-i') {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (flagsWithValues.has(arg)) {
|
|
52
|
+
if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
index += 1;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if ((/^-[ablnPRtV]+$/.test(arg) &&
|
|
59
|
+
[...arg.slice(1)].every((flag) => 'ablnPRtV'.includes(flag))) ||
|
|
60
|
+
/^-i(?:TCP|UDP)?(?::\d+(?:-\d+)?)?$/.test(arg) ||
|
|
61
|
+
/^-s(?:TCP|UDP):[A-Za-z]+$/.test(arg) ||
|
|
62
|
+
/^-(?:c|d|F|g|p|T|u).+$/.test(arg) ||
|
|
63
|
+
/^\+(?:d|D).+$/.test(arg)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
export function validPs(args) {
|
|
71
|
+
const bareForms = new Set(['aux', 'ax', 'x', 'u']);
|
|
72
|
+
const flagsWithoutValues = new Set(['-A', '-a', '-d', '-e', '-f', '-j', '-l', '-T', '-x']);
|
|
73
|
+
const flagsWithValues = new Set([
|
|
74
|
+
'--format',
|
|
75
|
+
'--group',
|
|
76
|
+
'--pid',
|
|
77
|
+
'--ppid',
|
|
78
|
+
'--sort',
|
|
79
|
+
'--tty',
|
|
80
|
+
'--user',
|
|
81
|
+
]);
|
|
82
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
83
|
+
const arg = args[index] ?? '';
|
|
84
|
+
if (bareForms.has(arg) || flagsWithoutValues.has(arg)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (flagsWithValues.has(arg)) {
|
|
88
|
+
if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
index += 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (/^(?:--format|--group|--pid|--ppid|--sort|--tty|--user)=.+$/.test(arg)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (/^-[^-]/.test(arg)) {
|
|
98
|
+
const noValue = new Set(['A', 'a', 'd', 'e', 'f', 'j', 'l', 'T', 'x']);
|
|
99
|
+
const withValue = new Set(['G', 'g', 'N', 'o', 'p', 't', 'U', 'u']);
|
|
100
|
+
const characters = arg.slice(1);
|
|
101
|
+
let valid = true;
|
|
102
|
+
for (let optionIndex = 0; optionIndex < characters.length; optionIndex += 1) {
|
|
103
|
+
const option = characters[optionIndex] ?? '';
|
|
104
|
+
if (noValue.has(option)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (withValue.has(option)) {
|
|
108
|
+
if (optionIndex === characters.length - 1) {
|
|
109
|
+
if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
index += 1;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
valid = false;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
if (valid) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
export function validFilesystemTest(head, args) {
|
|
128
|
+
const operands = head === '[' && args.at(-1) === ']' ? args.slice(0, -1) : args;
|
|
129
|
+
if (head === '[' && args.at(-1) !== ']') {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return (operands.length === 2 &&
|
|
133
|
+
new Set(['-b', '-c', '-d', '-e', '-f', '-h', '-L', '-r', '-s', '-w', '-x']).has(operands[0] ?? '') &&
|
|
134
|
+
Boolean(operands[1]));
|
|
135
|
+
}
|
|
136
|
+
export function bashSyntaxTarget(args) {
|
|
137
|
+
let syntaxOnly = false;
|
|
138
|
+
let script = null;
|
|
139
|
+
const shortFlags = new Set('abefhkmnptuvxBCEHPT'.split(''));
|
|
140
|
+
const longFlags = new Set([
|
|
141
|
+
'--debugger',
|
|
142
|
+
'--dump-po-strings',
|
|
143
|
+
'--dump-strings',
|
|
144
|
+
'--help',
|
|
145
|
+
'--login',
|
|
146
|
+
'--noediting',
|
|
147
|
+
'--noprofile',
|
|
148
|
+
'--norc',
|
|
149
|
+
'--posix',
|
|
150
|
+
'--restricted',
|
|
151
|
+
'--verbose',
|
|
152
|
+
'--version',
|
|
153
|
+
]);
|
|
154
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
155
|
+
const arg = args[index] ?? '';
|
|
156
|
+
if (arg === '-c' || arg === '-lc') {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
if (arg === '-O' || arg === '+O' || arg === '--rcfile' || arg === '--init-file') {
|
|
160
|
+
if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
index += 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if ((arg.startsWith('-O') && arg.length > 2) ||
|
|
167
|
+
(arg.startsWith('+O') && arg.length > 2) ||
|
|
168
|
+
(arg.startsWith('--rcfile=') && arg.length > '--rcfile='.length) ||
|
|
169
|
+
(arg.startsWith('--init-file=') && arg.length > '--init-file='.length)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (longFlags.has(arg)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (/^[+-][A-Za-z]+$/.test(arg)) {
|
|
176
|
+
const options = arg.slice(1);
|
|
177
|
+
if ([...options].some((option) => !shortFlags.has(option))) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
syntaxOnly ||= options.includes('n');
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (arg === '--') {
|
|
184
|
+
script = args[index + 1] ?? null;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
if (arg.startsWith('-') || arg.startsWith('+')) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
script = arg;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
return syntaxOnly ? script : null;
|
|
194
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type peelPackageExecArgv } from '../../package-exec.js';
|
|
2
|
+
import type { ShellEffectRequirement } from '../../shell-build.js';
|
|
3
|
+
export declare function decodePackageExec(peel: NonNullable<ReturnType<typeof peelPackageExecArgv>>, cwd: string, repoRoot: string, segment: string): ShellEffectRequirement[];
|
|
4
|
+
export declare function packageExecInnerIsMetadata(peel: NonNullable<ReturnType<typeof peelPackageExecArgv>>): boolean;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { classifyPackageAcquisitionSpec, resolveLocalBin, } from '../../package-exec.js';
|
|
2
|
+
import { requirement } from '../requirement.js';
|
|
3
|
+
export function decodePackageExec(peel, cwd, repoRoot, segment) {
|
|
4
|
+
if (peel.opaque) {
|
|
5
|
+
return [
|
|
6
|
+
requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
|
|
7
|
+
peel.reason,
|
|
8
|
+
...peel.signals,
|
|
9
|
+
]),
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
if (peel.reason === 'npx_wrapper_readonly') {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const binHead = peel.innerTokens[0] ?? '';
|
|
16
|
+
const metadataOnly = packageExecInnerIsMetadata(peel);
|
|
17
|
+
const local = !peel.forceAcquire ? resolveLocalBin(binHead, cwd, repoRoot) : null;
|
|
18
|
+
if (local) {
|
|
19
|
+
return metadataOnly
|
|
20
|
+
? [
|
|
21
|
+
requirement('process.exec', 'process.exec', { kind: 'executable', command: local.path, operation: 'inspect' }, segment, ['package_exec.delegate', 'package_exec.local_bin_resolved']),
|
|
22
|
+
]
|
|
23
|
+
: [];
|
|
24
|
+
}
|
|
25
|
+
const networkResources = peel.acquisitionSpecs
|
|
26
|
+
.map((spec) => classifyPackageAcquisitionSpec(spec))
|
|
27
|
+
.filter((source) => source.kind !== 'local')
|
|
28
|
+
.map((source) => source.kind === 'registry'
|
|
29
|
+
? {
|
|
30
|
+
kind: 'network',
|
|
31
|
+
host: 'registry.npmjs.org',
|
|
32
|
+
protocol: 'registry',
|
|
33
|
+
mode: 'read',
|
|
34
|
+
payload: 'none',
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
kind: 'network',
|
|
38
|
+
host: source.host,
|
|
39
|
+
...(source.port ? { port: source.port } : {}),
|
|
40
|
+
protocol: source.protocol,
|
|
41
|
+
mode: 'read',
|
|
42
|
+
payload: 'none',
|
|
43
|
+
});
|
|
44
|
+
if (networkResources.length === 0) {
|
|
45
|
+
networkResources.push({
|
|
46
|
+
kind: 'network',
|
|
47
|
+
host: 'registry.npmjs.org',
|
|
48
|
+
protocol: 'registry',
|
|
49
|
+
mode: 'read',
|
|
50
|
+
payload: 'none',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return [
|
|
54
|
+
...networkResources.map((resource) => requirement('network.acquire', 'network.connect', resource, segment, [
|
|
55
|
+
'package_acquire_possible',
|
|
56
|
+
])),
|
|
57
|
+
requirement('fs.write', 'fs.write', { kind: 'package-cache', manager: peel.launcher === 'pnpm' ? 'pnpm' : 'npm' }, segment, ['package_cache_write']),
|
|
58
|
+
...(metadataOnly
|
|
59
|
+
? [
|
|
60
|
+
requirement('process.exec', 'process.exec', { kind: 'executable', command: binHead || peel.launcher, operation: 'inspect' }, segment, ['package_exec.delegate']),
|
|
61
|
+
]
|
|
62
|
+
: []),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
export function packageExecInnerIsMetadata(peel) {
|
|
66
|
+
const args = peel.innerTokens.slice(1);
|
|
67
|
+
return (args.length > 0 && args.every((arg) => ['--help', '--version', '-V', '-h', '-v'].includes(arg)));
|
|
68
|
+
}
|