gologin 3.0.7 → 3.0.8

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Combined changelog for GoLogin node.js SDK
4
4
 
5
+ ## [3.0.8] 2026-11-12
6
+
7
+ ### Features
8
+
9
+ * Added `isCloudBrowser` option for GoLogin cloud browser. When enabled, profile `launchArguments` that run other programs (`*-cmd-prefix`, `*-launcher`, `--browser-subprocess-path`), open remote debugging, override the proxy or DNS, or point at paths (`--user-data-dir`, `--load-extension`, ...) are dropped and logged with proxy values redacted. Off by default; local launches are unchanged
10
+
5
11
  ## [3.0.7] 2026-10-15
6
12
 
7
13
  ### Fixes
package/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export type GoLoginOptions = {
8
8
  tmpdir?: string;
9
9
  waitWebsocket?: boolean;
10
10
  isCloudHeadless?: boolean;
11
+ isCloudBrowser?: boolean;
11
12
  skipOrbitaHashChecking?: boolean;
12
13
  uploadCookiesToServer?: boolean;
13
14
  writeCookiesFromServer?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "3.0.7",
3
+ "version": "3.0.8",
4
4
  "description": "A high-level API to control Orbita browser over GoLogin API",
5
5
  "types": "./index.d.ts",
6
6
  "main": "./src/index.js",
@@ -59,7 +59,7 @@
59
59
  },
60
60
  "scripts": {
61
61
  "test": "npm run test:unit && npm run test:e2e",
62
- "test:unit": "echo \"No unit tests specified\" && exit 0",
62
+ "test:unit": "node --test 'test/unit/*.test.js'",
63
63
  "test:e2e": "node test/e2e/run-tests.js",
64
64
  "test:e2e:local": "HEADLESS=false npm run test:e2e",
65
65
  "format": "npx prettier --single-quote src/* --write",
package/src/gologin.js CHANGED
@@ -15,6 +15,7 @@ import { checkAutoLang, getIntlProfileConfig, securedOrbitaOpts } from './utils/
15
15
  import { API_URL, ensureDirectoryExists, FALLBACK_API_URL, getOsAdvanced } from './utils/common.js';
16
16
  import { STORAGE_GATEWAY_BASE_URL } from './utils/constants.js';
17
17
  import { checkSocksProxy, makeRequest } from './utils/http.js';
18
+ import { describeBlockedArgument, filterCloudLaunchArguments } from './utils/launch-arguments.js';
18
19
  import {
19
20
  // ensureSentryInitialized,
20
21
  loadBrowserChecker,
@@ -69,6 +70,7 @@ export class GoLogin {
69
70
  this.isFirstSession = false;
70
71
  this.keepIndexedDb = false;
71
72
  this.isCloudHeadless = options.isCloudHeadless ?? true;
73
+ this.isCloudBrowser = options.isCloudBrowser ?? false;
72
74
  this.storageGatewayUrl = `${STORAGE_GATEWAY_BASE_URL}/upload`;
73
75
 
74
76
  this.tmpdir = tmpdir();
@@ -494,6 +496,17 @@ export class GoLogin {
494
496
 
495
497
  this.keepIndexedDb = profile.storage?.indexedDb === true;
496
498
  this.profileLaunchArguments = (profile.launchArguments || '').split(' ').filter(Boolean);
499
+ if (this.isCloudBrowser && this.profileLaunchArguments.length) {
500
+ const { allowed, blocked } = filterCloudLaunchArguments(this.profileLaunchArguments);
501
+ if (blocked.length) {
502
+ console.warn('[launch-arguments] cloud browser: dropped profile launch arguments', {
503
+ profileId: this.profile_id,
504
+ blocked: blocked.map(describeBlockedArgument),
505
+ });
506
+ }
507
+
508
+ this.profileLaunchArguments = allowed;
509
+ }
497
510
 
498
511
  const { navigator = {}, fonts, os: profileOs } = profile;
499
512
  this.fontsMasking = fonts?.enableMasking;
@@ -629,6 +642,7 @@ export class GoLogin {
629
642
  } else {
630
643
  this.browserLang = checkAutoLangResult;
631
644
  }
645
+
632
646
  const prefsToWrite = Object.assign(preferences, { gologin });
633
647
  if (this.customChromeFrame === false) {
634
648
  prefsToWrite.browser = {
@@ -0,0 +1,83 @@
1
+ // Profile launch arguments (profile.launchArguments) are free text set by the user and
2
+ // appended to Orbita's command line. In GoLogin's cloud browser that command line runs
3
+ // on our servers, so some switches must not come from the user:
4
+ // - switches that make Chrome run another program in place of its own processes
5
+ // (with --no-sandbox that is arbitrary command execution inside the pod),
6
+ // - remote debugging / DevTools access, which the cloud sets up itself,
7
+ // - proxy and DNS overrides, which would bypass the profile's proxy,
8
+ // - filesystem paths (profile dir, cache, logs, extensions loaded from a path).
9
+ // Chrome uses the last value of a repeated switch, so a user switch would also override
10
+ // the SDK's own. Only applied when the SDK runs with isCloudBrowser: true.
11
+
12
+ const BLOCKED_SWITCHES = new Set([
13
+ 'browser-subprocess-path',
14
+ 'type',
15
+ 'remote-debugging-port',
16
+ 'remote-debugging-pipe',
17
+ 'remote-debugging-address',
18
+ 'remote-debugging-io-pipes',
19
+ 'remote-debugging-targets',
20
+ 'remote-allow-origins',
21
+ 'proxy-server',
22
+ 'proxy-pac-url',
23
+ 'no-proxy-server',
24
+ 'proxy-bypass-list',
25
+ 'proxy-auto-detect',
26
+ 'host-resolver-rules',
27
+ 'host-rules',
28
+ 'user-data-dir',
29
+ 'disk-cache-dir',
30
+ 'crash-dumps-dir',
31
+ 'log-file',
32
+ 'load-extension',
33
+ ]);
34
+
35
+ // Any switch that wraps or replaces a child process launch:
36
+ // --renderer-cmd-prefix, --utility-cmd-prefix, --gpu-launcher, --ppapi-plugin-launcher, ...
37
+ const BLOCKED_SWITCH_PATTERNS = [/-cmd-prefix$/, /-launcher$/, /^nacl-gdb/];
38
+
39
+ // Chrome on Linux accepts both "--name" and "-name"; switch names are case-sensitive
40
+ // there, but lowercase them so no spelling slips through.
41
+ export const launchSwitchName = (arg) => {
42
+ const match = /^--?([^=]+)/.exec(arg);
43
+
44
+ return match ? match[1].trim().toLowerCase() : null;
45
+ };
46
+
47
+ const isBlockedSwitch = (name) =>
48
+ BLOCKED_SWITCHES.has(name) || BLOCKED_SWITCH_PATTERNS.some((pattern) => pattern.test(name));
49
+
50
+ // Returns the arguments that may be passed to Chrome and the ones that were removed.
51
+ // A bare "--" is removed too: Chrome would treat every argument after it, including the
52
+ // SDK's own switches, as a URL.
53
+ export const filterCloudLaunchArguments = (args) => {
54
+ const allowed = [];
55
+ const blocked = [];
56
+ for (const arg of args) {
57
+ if (arg === '--') {
58
+ blocked.push(arg);
59
+ continue;
60
+ }
61
+
62
+ const name = launchSwitchName(arg);
63
+ if (name !== null && isBlockedSwitch(name)) {
64
+ blocked.push(arg);
65
+ continue;
66
+ }
67
+
68
+ allowed.push(arg);
69
+ }
70
+
71
+ return { allowed, blocked };
72
+ };
73
+
74
+ // For logs: keep what was tried (e.g. which program a *-cmd-prefix pointed at), but not
75
+ // proxy credentials.
76
+ export const describeBlockedArgument = (arg) => {
77
+ const name = launchSwitchName(arg);
78
+ if (name && /proxy|host-rules|host-resolver-rules/.test(name) && arg.includes('=')) {
79
+ return `${arg.slice(0, arg.indexOf('='))}=<redacted>`;
80
+ }
81
+
82
+ return arg;
83
+ };
@@ -0,0 +1,96 @@
1
+ import assert from 'node:assert/strict';
2
+ import { test } from 'node:test';
3
+
4
+ import {
5
+ describeBlockedArgument,
6
+ filterCloudLaunchArguments,
7
+ launchSwitchName,
8
+ } from '../../src/utils/launch-arguments.js';
9
+
10
+ // The arguments seen on profile 6aebd85725714ca1d36e3531 (2026-11-12).
11
+ const SEEN_IN_CLOUD = [
12
+ '--load-extension=/home/neko/.gologin/extensions/user-extensions/glpextkeepalive000000000001',
13
+ '--utility-cmd-prefix=/usr/bin/python3',
14
+ ];
15
+
16
+ test('drops the arguments seen in the cloud', () => {
17
+ const { allowed, blocked } = filterCloudLaunchArguments(SEEN_IN_CLOUD);
18
+ assert.deepEqual(allowed, []);
19
+ assert.deepEqual(blocked, SEEN_IN_CLOUD);
20
+ });
21
+
22
+ test('drops every switch that runs another program', () => {
23
+ const args = [
24
+ '--renderer-cmd-prefix=/bin/sh -c id',
25
+ '--utility-cmd-prefix=/usr/bin/python3',
26
+ '--gpu-cmd-prefix=x',
27
+ '--zygote-cmd-prefix=x',
28
+ '--gpu-launcher=/bin/sh',
29
+ '--plugin-launcher=x',
30
+ '--ppapi-plugin-launcher=x',
31
+ '--browser-subprocess-path=/tmp/x',
32
+ '--nacl-gdb=/tmp/x',
33
+ '--nacl-gdb-script=/tmp/x',
34
+ '--type=renderer',
35
+ ];
36
+ assert.deepEqual(filterCloudLaunchArguments(args).allowed, []);
37
+ });
38
+
39
+ test('drops remote debugging, proxy, DNS and path overrides', () => {
40
+ const args = [
41
+ '--remote-debugging-port=9222',
42
+ '--remote-debugging-address=0.0.0.0',
43
+ '--remote-debugging-pipe',
44
+ '--remote-allow-origins=*',
45
+ '--proxy-server=http://user:pass@1.2.3.4:8080',
46
+ '--proxy-pac-url=http://x/pac',
47
+ '--no-proxy-server',
48
+ '--proxy-bypass-list=*',
49
+ '--host-resolver-rules=MAP * 1.2.3.4',
50
+ '--host-rules=MAP * 1.2.3.4',
51
+ '--user-data-dir=/tmp/other',
52
+ '--disk-cache-dir=/tmp/c',
53
+ '--crash-dumps-dir=/tmp/d',
54
+ '--log-file=/etc/passwd',
55
+ '--load-extension=/tmp/ext',
56
+ ];
57
+ assert.deepEqual(filterCloudLaunchArguments(args).allowed, []);
58
+ });
59
+
60
+ test('single dash and upper case spellings are caught too', () => {
61
+ const args = ['-utility-cmd-prefix=/usr/bin/python3', '--Renderer-Cmd-Prefix=/bin/sh', '-PROXY-SERVER=x'];
62
+ assert.deepEqual(filterCloudLaunchArguments(args).allowed, []);
63
+ });
64
+
65
+ test('a bare -- is dropped: it would turn the SDK switches after it into URLs', () => {
66
+ assert.deepEqual(filterCloudLaunchArguments(['--', '--restore-last-session']), {
67
+ allowed: ['--restore-last-session'],
68
+ blocked: ['--'],
69
+ });
70
+ });
71
+
72
+ test('ordinary launch arguments pass through unchanged and in order', () => {
73
+ const args = [
74
+ '--lang=de-DE',
75
+ '--start-maximized',
76
+ '--window-size=1280,800',
77
+ '--disable-features=Translate',
78
+ '--enable-features=WebGPU',
79
+ '--force-device-scale-factor=1.25',
80
+ '--user-agent=Mozilla/5.0 test',
81
+ '--disable-web-security',
82
+ 'https://example.com/start',
83
+ ];
84
+ assert.deepEqual(filterCloudLaunchArguments(args), { allowed: args, blocked: [] });
85
+ });
86
+
87
+ test('switch names are read without the value', () => {
88
+ assert.equal(launchSwitchName('--utility-cmd-prefix=/usr/bin/python3'), 'utility-cmd-prefix');
89
+ assert.equal(launchSwitchName('-No-Proxy-Server'), 'no-proxy-server');
90
+ assert.equal(launchSwitchName('https://example.com'), null);
91
+ });
92
+
93
+ test('log description hides proxy credentials but keeps what was run', () => {
94
+ assert.equal(describeBlockedArgument('--proxy-server=http://user:pass@1.2.3.4:8080'), '--proxy-server=<redacted>');
95
+ assert.equal(describeBlockedArgument('--utility-cmd-prefix=/usr/bin/python3'), '--utility-cmd-prefix=/usr/bin/python3');
96
+ });