pi-landstrip 0.3.2 → 0.3.3

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.
Files changed (3) hide show
  1. package/index.ts +51 -32
  2. package/package.json +2 -2
  3. package/sandbox.json +1 -0
package/index.ts CHANGED
@@ -47,6 +47,7 @@ interface SandboxFilesystemConfig {
47
47
  }
48
48
 
49
49
  interface SandboxNetworkConfig {
50
+ allowNetwork: boolean;
50
51
  allowLocalBinding: boolean;
51
52
  allowAllUnixSockets: boolean;
52
53
  allowUnixSockets: string[];
@@ -62,10 +63,11 @@ interface SandboxConfig {
62
63
 
63
64
  interface LandstripPolicy {
64
65
  network: {
66
+ allowNetwork: boolean;
65
67
  allowLocalBinding: boolean;
66
68
  allowAllUnixSockets: boolean;
67
69
  allowUnixSockets: string[];
68
- httpProxyPort: number;
70
+ httpProxyPort?: number;
69
71
  };
70
72
  filesystem: SandboxFilesystemConfig;
71
73
  }
@@ -78,12 +80,13 @@ interface LandstripErrorResponse {
78
80
  message: string;
79
81
  }
80
82
 
81
- const LANDSTRIP_VERSION = [0, 9, 5] as const;
83
+ const LANDSTRIP_VERSION = [0, 9, 7] as const;
82
84
  const SUPPORTED_PLATFORMS = new Set<NodeJS.Platform>(['linux', 'darwin', 'win32']);
83
85
 
84
86
  const DEFAULT_CONFIG: SandboxConfig = {
85
87
  enabled: true,
86
88
  network: {
89
+ allowNetwork: false,
87
90
  allowLocalBinding: false,
88
91
  allowAllUnixSockets: false,
89
92
  allowUnixSockets: [],
@@ -694,15 +697,16 @@ export function createLandstripIntegration(
694
697
  return true;
695
698
  }
696
699
 
697
- function buildLandstripPolicy(cwd: string, proxyPort: number): LandstripPolicy {
700
+ function buildLandstripPolicy(cwd: string, proxyPort: number | null): LandstripPolicy {
698
701
  const config = loadConfig(cwd);
699
702
 
700
703
  return {
701
704
  network: {
705
+ allowNetwork: config.network.allowNetwork,
702
706
  allowLocalBinding: config.network.allowLocalBinding,
703
707
  allowAllUnixSockets: config.network.allowAllUnixSockets,
704
708
  allowUnixSockets: config.network.allowUnixSockets,
705
- httpProxyPort: proxyPort,
709
+ ...(proxyPort !== null ? { httpProxyPort: proxyPort } : {}),
706
710
  },
707
711
  filesystem: {
708
712
  denyRead: config.filesystem.denyRead,
@@ -713,7 +717,7 @@ export function createLandstripIntegration(
713
717
  };
714
718
  }
715
719
 
716
- function writePolicyFile(cwd: string, proxyPort: number): { dir: string; path: string } {
720
+ function writePolicyFile(cwd: string, proxyPort: number | null): { dir: string; path: string } {
717
721
  const dir = mkdtempSync(join(tmpdir(), 'pi-landstrip-'));
718
722
  const path = join(dir, 'policy.json');
719
723
  writeFileSync(
@@ -858,8 +862,11 @@ export function createLandstripIntegration(
858
862
  if (!existsSync(cwd)) throw new Error(`Working directory does not exist: ${cwd}`);
859
863
 
860
864
  const { shell, args } = getShellConfig(SettingsManager.create(cwd).getShellPath());
861
- const proxy = await startProxy(ctx, cwd);
862
- const policy = writePolicyFile(cwd, proxy.port);
865
+ const config = loadConfig(cwd);
866
+ const allowNetwork = config.network.allowNetwork;
867
+ const proxy = allowNetwork ? null : await startProxy(ctx, cwd);
868
+ const proxyPort = proxy ? proxy.port : null;
869
+ const policy = writePolicyFile(cwd, proxyPort);
863
870
  const landstripArgs = ['-p', policy.path, shell, ...args, command];
864
871
 
865
872
  return new Promise((resolvePromise, reject) => {
@@ -872,13 +879,13 @@ export function createLandstripIntegration(
872
879
  cleaned = true;
873
880
  if (timeoutHandle) clearTimeout(timeoutHandle);
874
881
  signal?.removeEventListener('abort', onAbort);
875
- void proxy.stop();
882
+ void proxy?.stop();
876
883
  rmSync(policy.dir, { recursive: true, force: true });
877
884
  };
878
885
 
879
886
  const child = spawn(binaryPath(), landstripArgs, {
880
887
  cwd,
881
- env: proxyEnv(env, proxy.port),
888
+ env: allowNetwork ? { ...process.env, ...env } : proxyEnv(env, proxy!.port),
882
889
  detached: true,
883
890
  stdio: ['ignore', 'pipe', 'pipe'],
884
891
  });
@@ -1005,6 +1012,10 @@ export function createLandstripIntegration(
1005
1012
  }
1006
1013
 
1007
1014
  function warnIfAllDomainsAllowed(ctx: ExtensionContext, config: SandboxConfig): void {
1015
+ if (config.network.allowNetwork) {
1016
+ ctx.ui.notify('Network sandbox is disabled because network.allowNetwork is true.', 'warning');
1017
+ return;
1018
+ }
1008
1019
  if (!allowsAllDomains(config.network.allowedDomains)) return;
1009
1020
  ctx.ui.notify(
1010
1021
  'Network sandbox allows all domains because network.allowedDomains contains "*".',
@@ -1013,9 +1024,11 @@ export function createLandstripIntegration(
1013
1024
  }
1014
1025
 
1015
1026
  function enableStatus(ctx: ExtensionContext, config: SandboxConfig): void {
1016
- const networkLabel = allowsAllDomains(config.network.allowedDomains)
1017
- ? 'all domains'
1018
- : `${config.network.allowedDomains.length} domains`;
1027
+ const networkLabel = config.network.allowNetwork
1028
+ ? 'unrestricted'
1029
+ : allowsAllDomains(config.network.allowedDomains)
1030
+ ? 'all domains'
1031
+ : `${config.network.allowedDomains.length} domains`;
1019
1032
  ctx.ui.setStatus(
1020
1033
  'sandbox',
1021
1034
  ctx.ui.theme.fg(
@@ -1049,7 +1062,7 @@ export function createLandstripIntegration(
1049
1062
  if (!hasMinimumVersion(version, LANDSTRIP_VERSION)) {
1050
1063
  sandboxEnabled = false;
1051
1064
  sandboxReady = false;
1052
- ctx.ui.notify(`landstrip 0.9.5 or newer is required; found: ${version}`, 'error');
1065
+ ctx.ui.notify(`landstrip 0.9.7 or newer is required; found: ${version}`, 'error');
1053
1066
  return false;
1054
1067
  }
1055
1068
 
@@ -1093,18 +1106,21 @@ export function createLandstripIntegration(
1093
1106
 
1094
1107
  pi.on('user_bash', async (event, ctx) => {
1095
1108
  if (!sandboxEnabled || !sandboxReady) return;
1096
- if (!loadConfig(ctx.cwd).enabled) return;
1097
-
1098
- const blockedDomain = await preflightCommandDomains(event.command, ctx);
1099
- if (blockedDomain) {
1100
- return {
1101
- result: {
1102
- output: `Blocked: "${blockedDomain}" is not allowed by the sandbox. Use /sandbox to review your config.`,
1103
- exitCode: 1,
1104
- cancelled: false,
1105
- truncated: false,
1106
- },
1107
- };
1109
+ const config = loadConfig(ctx.cwd);
1110
+ if (!config.enabled) return;
1111
+
1112
+ if (!config.network.allowNetwork) {
1113
+ const blockedDomain = await preflightCommandDomains(event.command, ctx);
1114
+ if (blockedDomain) {
1115
+ return {
1116
+ result: {
1117
+ output: `Blocked: "${blockedDomain}" is not allowed by the sandbox. Use /sandbox to review your config.`,
1118
+ exitCode: 1,
1119
+ cancelled: false,
1120
+ truncated: false,
1121
+ },
1122
+ };
1123
+ }
1108
1124
  }
1109
1125
 
1110
1126
  return { operations: createLandstripBashOps(ctx) };
@@ -1119,12 +1135,14 @@ export function createLandstripIntegration(
1119
1135
  const { globalPath, projectPath } = getConfigPaths(ctx.cwd);
1120
1136
 
1121
1137
  if (sandboxReady && isToolCallEventType('bash', event)) {
1122
- const blockedDomain = await preflightCommandDomains(event.input.command, ctx);
1123
- if (blockedDomain) {
1124
- return {
1125
- block: true,
1126
- reason: `Network access to "${blockedDomain}" is blocked by the sandbox.`,
1127
- };
1138
+ if (!config.network.allowNetwork) {
1139
+ const blockedDomain = await preflightCommandDomains(event.input.command, ctx);
1140
+ if (blockedDomain) {
1141
+ return {
1142
+ block: true,
1143
+ reason: `Network access to "${blockedDomain}" is blocked by the sandbox.`,
1144
+ };
1145
+ }
1128
1146
  }
1129
1147
  }
1130
1148
 
@@ -1231,7 +1249,8 @@ export function createLandstripIntegration(
1231
1249
  ` Global config: ${globalPath}`,
1232
1250
  ` landstrip: ${binaryPath()}`,
1233
1251
  '',
1234
- 'Network (bash through HTTP proxy):',
1252
+ `Network (bash ${config.network.allowNetwork ? 'unrestricted' : 'through HTTP proxy'}):`,
1253
+ ` Allow network: ${config.network.allowNetwork}`,
1235
1254
  ` Allowed domains: ${config.network.allowedDomains.join(', ') || '(none)'}`,
1236
1255
  ` Denied domains: ${config.network.deniedDomains.join(', ') || '(none)'}`,
1237
1256
  ...(sessionAllowedDomains.length > 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Landlock-based sandboxing for pi with interactive permission prompts",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@earendil-works/pi-tui": "^0.78.0",
34
- "@jarkkojs/landstrip": "^0.9.5"
34
+ "@jarkkojs/landstrip": "^0.9.7"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@earendil-works/pi-coding-agent": "^0.78.0",
package/sandbox.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "enabled": true,
3
3
  "network": {
4
+ "allowNetwork": false,
4
5
  "allowLocalBinding": true,
5
6
  "allowAllUnixSockets": false,
6
7
  "allowUnixSockets": [],