Compare commits

...

6 Commits

Author SHA1 Message Date
undergroundwires
abec9def07 mac, linux, win: fix dead URLs and improve docs
This commit fixes dead URLs and updates documentation references,
improving accuracy and reliability.

Key changes:

- Fix dead URLs by using archived snapshots when they are detected as
  down by tests.
- Update URLs to their new redirected locations.

Other supporting changes:

- Introduce long URLs for `archive.ph` links to retain the original
  URLs within the documentation. It simplifies the maintenance by
  removing the need to document the original locations along with the
  short URLs.
- Improve some of the documentation to use more current sources,
  replacing the outdated ones.
2024-03-22 17:27:15 +01:00
undergroundwires
b71ad797a3 win: fix VSCode manual update switch script #312
This commit addresses a regression from refactoring in #215.

It restores YAML escape mechanism with quoting around 'manual' in the
`powerShellValue` attribute to ensure PowerShell interprets the value
correctly.

This change is documented with a comment to avoid future omissions.

This reverts commit c27172c32e.
2024-03-20 08:38:23 +01:00
undergroundwires
ec34ac1124 Fix tooltip styling inconsistency
This commit fixes inconsistent tooltip styling by setting the font
explicitly on the tooltip container to ensure uniform tooltip fonts.

As tooltip is rendered inside the parent elements' DOM, styling parent
element's font was also styling the font's font due to style
propogation, but setting fonts explicitly on tooltip ensure this does
not happen.
2024-03-19 09:09:29 +01:00
undergroundwires
840adf9429 Bump Electron to latest and use native ESM
This commit bumps Electron and related dependencies to their latest
versions to leverage native ESM support. It adjusts build configuration
to use native ESM support instead of relying on CommonJS bundling.

Key changes:

- Bump Electron to latest v29.
  Electron v28 ships with native ESM/ECMAScript modules support.
  Details on Electron ESM support:
    - electron/electron#21457
    - electron/electron#37535
- Bump `electron-builder` to latest v24.13.
  `electron-builder` is used to package and publish the application.
  It supports ESM since 24.10.
  Details on `electron-builder` ESM support:
    - electron-userland/electron-builder#7936
    - electron-userland/electron-builder#7935
- Bump `electron-log` to latest v5.1.
  `electron-log` supports ESM since version 5.0.4.
  Details on `electron-log` ESM support:
    - megahertz/electron-log#390.
- Change `electron-vite` configuration to bundle as ESM instead of
  CommonJS to leverage Electron's native ESM support.

Other supporting changes:

- Add type hint for electron-builder configuration file.
- Update import statements for `electron-updater` as it still is a
  CommonJS module and does not support ESM.
  Details:
    - electron-userland/electron-builder#7976
- Improve `electron-builder` configuration file to dynamically locate
  main entry files, supporting various JavaScript file extensions
  (`.js`, `.mjs` and `.cjs`) to facilitate easier future changes.
- Change comment about Electron process-specific module alias
  registration. This issue has been fixed in `electron-vite`, but
  subpath module imports for Electron still do not work when building
  tests (`npm run test:unit`).
  Details:
   - alex8088/electron-vite#372
- Add `electron-log` in bundling process instead of externalizing to
  workaround Electron ESM loader issues with subpath imports (inability
  to do `electron-log/main`).
  Details:
    - alex8088/electron-vite#401
    - electron/electron#41241
- Improve desktop runtime error checks' assertion message for better
  clarity.
2024-03-18 11:55:56 +01:00
undergroundwires
5eff3a0488 win: improve OneDrive data deletion safety
This commit improves the safety mechanisms in the script for deleting
OneDrive user data on Windows.

Key changes:

- System Integrity Protection: The script now checks if user shell
  folders point to the OneDrive directory. If they do, it halts the
  deletion and provides guidance to the user. This ensures system
  stability is not compromised.
- Data Loss Prevention: The script will no longer delete files or
  non-empty folders. This precaution helps to avoid unintended data
  loss.

Other supporting changes:

- This script now covers OneDrive folders for multi-account users.
- Separation of concerns: The 'Remove OneDrive residual files' script is
  is divided into two distinct scripts for better maintainability and
  documentation clarity:
  1. 'Remove OneDrive user data and synced folders'
  2. 'Remove OneDrive installation files and cache'
- Fix an issue with the Windows 11 check in the 'Disable automatic
  OneDrive installation' revert script.
- Update related documentation with archived URLs for reliability.
- Fix indentation of OneDrive removal scripts.
2024-03-17 21:40:23 +01:00
undergroundwires
5abf8ff216 Improve URL checks to reduce false-negatives
This commit improves the URL health checking mechanism to reduce false
negatives.

- Treat all 2XX status codes as successful, addressing issues with codes
  like `204`.
- Improve URL matching to exclude URLs within Markdown inline code block
  and support URLs containing parentheses.
- Add `forceHttpGetForUrlPatterns` to customize HTTP method per URL to
  allow verifying URLs behind CDN/WAFs that do not respond to HTTP HEAD.
- Send the Host header for improved handling of webpages behind proxies.
- Improve formatting and context for output messages.
- Fix the defaulting options for redirects and cookie handling.
- Update the user agent pool to modern browsers and platforms.
- Add support for randomizing TLS fingerprint to mimic various clients
  better, improving the effectiveness of checks. However, this is not
  fully supported by Node.js's HTTP client; see nodejs/undici#1983 for
  more details.
- Use `AbortSignal` instead of `AbortController` as more modern and
  simpler way to handle timeouts.
2024-03-16 18:15:34 +01:00
37 changed files with 1455 additions and 1125 deletions

View File

@@ -35,8 +35,8 @@ Key attributes of a good script:
## Documentation ## Documentation
- Use credible and reputable sources for references. - Use credible and reputable sources for references.
- Use archived links by using [archive.org](https://archive.org) or [archive.today](https://archive.today). - Use archived links by using [archive.org](https://archive.org) or [archive.ph](https://archive.ph).
- Format archive.today links fully, for example: `https://archive.today/YYYYMMDDhhmmss/https://privacy.sexy`. - Format archive.today links fully, for example: `https://archive.ph/YYYYMMDDhhmmss/https://privacy.sexy`.
- Explain the default behavior if the script is not executed. - Explain the default behavior if the script is not executed.
## Shared functions ## Shared functions

View File

@@ -1,8 +1,13 @@
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
const { join } = require('node:path'); const { join } = require('node:path');
const { readdirSync } = require('fs');
const { electronBundled, electronUnbundled } = require('./dist-dirs.json'); const { electronBundled, electronUnbundled } = require('./dist-dirs.json');
/**
* @type {import('electron-builder').Configuration}
* @see https://www.electron.build/configuration/configuration
*/
module.exports = { module.exports = {
// Common options // Common options
publish: { publish: {
@@ -14,7 +19,9 @@ module.exports = {
output: electronBundled, output: electronBundled,
}, },
extraMetadata: { extraMetadata: {
main: join(electronUnbundled, 'main/index.cjs'), // do not `path.resolve`, it expects a relative path main: findMainEntryFile(
join(electronUnbundled, 'main'), // do not `path.resolve`, it expects a relative path
),
}, },
// Windows // Windows
@@ -41,3 +48,15 @@ module.exports = {
artifactName: '${name}-${version}.${ext}', artifactName: '${name}-${version}.${ext}',
}, },
}; };
/**
* Finds by accommodating different JS file extensions and module formats.
*/
function findMainEntryFile(parentDirectory) {
const files = readdirSync(parentDirectory);
const entryFile = files.find((file) => /^index\.(cjs|mjs|js)$/.test(file));
if (!entryFile) {
throw new Error(`Main entry file not found in ${parentDirectory}.`);
}
return join(parentDirectory, entryFile);
}

View File

@@ -14,7 +14,7 @@ const ELECTRON_DIST_SUBDIRECTORIES = {
renderer: resolveElectronDistSubdirectory('renderer'), renderer: resolveElectronDistSubdirectory('renderer'),
}; };
process.env.ELECTRON_ENTRY = resolve(ELECTRON_DIST_SUBDIRECTORIES.main, 'index.cjs'); process.env.ELECTRON_ENTRY = resolve(ELECTRON_DIST_SUBDIRECTORIES.main, 'index.mjs');
export default defineConfig({ export default defineConfig({
main: getSharedElectronConfig({ main: getSharedElectronConfig({
@@ -54,13 +54,23 @@ function getSharedElectronConfig(options: {
}, },
rollupOptions: { rollupOptions: {
output: { output: {
// Mark: electron-esm-support format: 'es',
// This is needed so `type="module"` works
entryFileNames: '[name].cjs', // Ensure all generated files use '.mjs' for module consistency.
// Otherwise, preloader process get `.mjs` extension but main process get `.js` extension, see https://github.com/alex8088/electron-vite/issues/397.
entryFileNames: '[name].mjs',
}, },
}, },
}, },
plugins: [externalizeDepsPlugin()], plugins: [externalizeDepsPlugin({
exclude: [
// Keep 'electron-log' in bundling process.
// This is a workaround for inability of Electron's ESM loader to resolve subpath imports.
// Do not externalize `electron-log` so subpath imports such as `electron-log/main` works.
// See https://github.com/electron/electron/issues/41241, https://github.com/alex8088/electron-vite/issues/401
'electron-log',
],
})],
define: { define: {
...getClientEnvironmentVariables(), ...getClientEnvironmentVariables(),
}, },

209
package-lock.json generated
View File

@@ -13,7 +13,7 @@
"@juggle/resize-observer": "^3.4.0", "@juggle/resize-observer": "^3.4.0",
"@types/markdown-it": "^13.0.7", "@types/markdown-it": "^13.0.7",
"ace-builds": "^1.30.0", "ace-builds": "^1.30.0",
"electron-log": "^5.0.1", "electron-log": "^5.1.2",
"electron-progressbar": "^2.1.0", "electron-progressbar": "^2.1.0",
"electron-updater": "^6.1.4", "electron-updater": "^6.1.4",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
@@ -34,8 +34,8 @@
"@vue/test-utils": "^2.4.1", "@vue/test-utils": "^2.4.1",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"cypress": "^13.3.1", "cypress": "^13.3.1",
"electron": "^27.0.0", "electron": "^29.1.4",
"electron-builder": "^24.6.4", "electron-builder": "^24.13.3",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"electron-icon-builder": "^2.0.1", "electron-icon-builder": "^2.0.1",
"electron-vite": "^2.1.0", "electron-vite": "^2.1.0",
@@ -4879,19 +4879,6 @@
"electron-builder-squirrel-windows": "24.13.3" "electron-builder-squirrel-windows": "24.13.3"
} }
}, },
"node_modules/app-builder-lib/node_modules/builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/app-builder-lib/node_modules/form-data": { "node_modules/app-builder-lib/node_modules/form-data": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -5718,9 +5705,9 @@
} }
}, },
"node_modules/builder-util-runtime": { "node_modules/builder-util-runtime": {
"version": "9.2.1", "version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.1.tgz", "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-2rLv/uQD2x+dJ0J3xtsmI12AlRyk7p45TEbE/6o/fbb633e/S3pPgm+ct+JHsoY7r39dKHnGEFk/AASRFdnXmA==", "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dependencies": { "dependencies": {
"debug": "^4.3.4", "debug": "^4.3.4",
"sax": "^1.2.4" "sax": "^1.2.4"
@@ -5744,19 +5731,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/builder-util/node_modules/builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/builder-util/node_modules/chalk": { "node_modules/builder-util/node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -7140,19 +7114,6 @@
"dmg-license": "^1.0.11" "dmg-license": "^1.0.11"
} }
}, },
"node_modules/dmg-builder/node_modules/builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/dmg-builder/node_modules/fs-extra": { "node_modules/dmg-builder/node_modules/fs-extra": {
"version": "10.1.0", "version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -7347,14 +7308,14 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "27.0.0", "version": "29.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-27.0.0.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-29.1.4.tgz",
"integrity": "sha512-mr3Zoy82l8XKK/TgguE5FeNeHZ9KHXIGIpUMjbjZWIREfAv+X2Q3vdX6RG0Pmi1K23AFAxANXQezIHBA2Eypwg==", "integrity": "sha512-IWXys0SqgmIfrqXusUGQC0gGG7CCqA5vfmNsUMj8dFkAnK3lisKyjSESStWlrsste/OX/AAC5wsVlf23reUNnw==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^18.11.18", "@types/node": "^20.9.0",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
}, },
"bin": { "bin": {
@@ -7433,19 +7394,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/electron-builder/node_modules/builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/electron-builder/node_modules/chalk": { "node_modules/electron-builder/node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -7565,9 +7513,9 @@
} }
}, },
"node_modules/electron-log": { "node_modules/electron-log": {
"version": "5.0.1", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.0.1.tgz", "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.1.2.tgz",
"integrity": "sha512-x4wnwHg00h/onWQgjmvcdLV7Mrd9TZjxNs8LmXVpqvANDf4FsSs5wLlzOykWLcaFzR3+5hdVEQ8ctmrUxgHlPA==", "integrity": "sha512-Cpg4hAZ27yM9wzE77c4TvgzxzavZ+dVltCczParXN+Vb3jocojCSAuSMCVOI9fhFuuOR+iuu3tZLX1cu0y0kgQ==",
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
} }
@@ -7610,19 +7558,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/electron-publish/node_modules/builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/electron-publish/node_modules/chalk": { "node_modules/electron-publish/node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -7699,11 +7634,11 @@
"dev": true "dev": true
}, },
"node_modules/electron-updater": { "node_modules/electron-updater": {
"version": "6.1.4", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.1.4.tgz", "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.2.1.tgz",
"integrity": "sha512-yYAJc6RQjjV4WtInZVn+ZcLyXRhbVXoomKEfUUwDqIk5s2wxzLhWaor7lrNgxODyODhipjg4SVPMhJHi5EnsCA==", "integrity": "sha512-83eKIPW14qwZqUUM6wdsIRwVKZyjmHxQ4/8G+1C6iS5PdDt7b1umYQyj1/qPpH510GmHEQe4q0kCPe3qmb3a0Q==",
"dependencies": { "dependencies": {
"builder-util-runtime": "9.2.1", "builder-util-runtime": "9.2.4",
"fs-extra": "^10.1.0", "fs-extra": "^10.1.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lazy-val": "^1.0.5", "lazy-val": "^1.0.5",
@@ -7755,6 +7690,21 @@
} }
} }
}, },
"node_modules/electron/node_modules/@types/node": {
"version": "20.11.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/electron/node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "9.2.2", "version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -24107,16 +24057,6 @@
"temp-file": "^3.4.0" "temp-file": "^3.4.0"
}, },
"dependencies": { "dependencies": {
"builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"requires": {
"debug": "^4.3.4",
"sax": "^1.2.4"
}
},
"form-data": { "form-data": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -24751,16 +24691,6 @@
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
} }
}, },
"builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"requires": {
"debug": "^4.3.4",
"sax": "^1.2.4"
}
},
"chalk": { "chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -24815,9 +24745,9 @@
} }
}, },
"builder-util-runtime": { "builder-util-runtime": {
"version": "9.2.1", "version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.1.tgz", "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-2rLv/uQD2x+dJ0J3xtsmI12AlRyk7p45TEbE/6o/fbb633e/S3pPgm+ct+JHsoY7r39dKHnGEFk/AASRFdnXmA==", "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"requires": { "requires": {
"debug": "^4.3.4", "debug": "^4.3.4",
"sax": "^1.2.4" "sax": "^1.2.4"
@@ -25829,16 +25759,6 @@
"js-yaml": "^4.1.0" "js-yaml": "^4.1.0"
}, },
"dependencies": { "dependencies": {
"builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"requires": {
"debug": "^4.3.4",
"sax": "^1.2.4"
}
},
"fs-extra": { "fs-extra": {
"version": "10.1.0", "version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -25989,14 +25909,31 @@
} }
}, },
"electron": { "electron": {
"version": "27.0.0", "version": "29.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-27.0.0.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-29.1.4.tgz",
"integrity": "sha512-mr3Zoy82l8XKK/TgguE5FeNeHZ9KHXIGIpUMjbjZWIREfAv+X2Q3vdX6RG0Pmi1K23AFAxANXQezIHBA2Eypwg==", "integrity": "sha512-IWXys0SqgmIfrqXusUGQC0gGG7CCqA5vfmNsUMj8dFkAnK3lisKyjSESStWlrsste/OX/AAC5wsVlf23reUNnw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^18.11.18", "@types/node": "^20.9.0",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
},
"dependencies": {
"@types/node": {
"version": "20.11.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
"dev": true,
"requires": {
"undici-types": "~5.26.4"
}
},
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
}
} }
}, },
"electron-builder": { "electron-builder": {
@@ -26027,16 +25964,6 @@
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
} }
}, },
"builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"requires": {
"debug": "^4.3.4",
"sax": "^1.2.4"
}
},
"chalk": { "chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -26157,9 +26084,9 @@
} }
}, },
"electron-log": { "electron-log": {
"version": "5.0.1", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.0.1.tgz", "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.1.2.tgz",
"integrity": "sha512-x4wnwHg00h/onWQgjmvcdLV7Mrd9TZjxNs8LmXVpqvANDf4FsSs5wLlzOykWLcaFzR3+5hdVEQ8ctmrUxgHlPA==" "integrity": "sha512-Cpg4hAZ27yM9wzE77c4TvgzxzavZ+dVltCczParXN+Vb3jocojCSAuSMCVOI9fhFuuOR+iuu3tZLX1cu0y0kgQ=="
}, },
"electron-progressbar": { "electron-progressbar": {
"version": "2.1.0", "version": "2.1.0",
@@ -26193,16 +26120,6 @@
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
} }
}, },
"builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"requires": {
"debug": "^4.3.4",
"sax": "^1.2.4"
}
},
"chalk": { "chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -26263,11 +26180,11 @@
"dev": true "dev": true
}, },
"electron-updater": { "electron-updater": {
"version": "6.1.4", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.1.4.tgz", "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.2.1.tgz",
"integrity": "sha512-yYAJc6RQjjV4WtInZVn+ZcLyXRhbVXoomKEfUUwDqIk5s2wxzLhWaor7lrNgxODyODhipjg4SVPMhJHi5EnsCA==", "integrity": "sha512-83eKIPW14qwZqUUM6wdsIRwVKZyjmHxQ4/8G+1C6iS5PdDt7b1umYQyj1/qPpH510GmHEQe4q0kCPe3qmb3a0Q==",
"requires": { "requires": {
"builder-util-runtime": "9.2.1", "builder-util-runtime": "9.2.4",
"fs-extra": "^10.1.0", "fs-extra": "^10.1.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lazy-val": "^1.0.5", "lazy-val": "^1.0.5",

View File

@@ -37,7 +37,7 @@
"@juggle/resize-observer": "^3.4.0", "@juggle/resize-observer": "^3.4.0",
"@types/markdown-it": "^13.0.7", "@types/markdown-it": "^13.0.7",
"ace-builds": "^1.30.0", "ace-builds": "^1.30.0",
"electron-log": "^5.0.1", "electron-log": "^5.1.2",
"electron-progressbar": "^2.1.0", "electron-progressbar": "^2.1.0",
"electron-updater": "^6.1.4", "electron-updater": "^6.1.4",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
@@ -58,8 +58,8 @@
"@vue/test-utils": "^2.4.1", "@vue/test-utils": "^2.4.1",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"cypress": "^13.3.1", "cypress": "^13.3.1",
"electron": "^27.0.0", "electron": "^29.1.4",
"electron-builder": "^24.6.4", "electron-builder": "^24.13.3",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"electron-icon-builder": "^2.0.1", "electron-icon-builder": "^2.0.1",
"electron-vite": "^2.1.0", "electron-vite": "^2.1.0",

View File

@@ -44,8 +44,8 @@ function getBuildVerificationConfigs() {
'--electron-unbundled': { '--electron-unbundled': {
printDistDirScriptArgument: '--electron-unbundled', printDistDirScriptArgument: '--electron-unbundled',
filePatterns: [ filePatterns: [
/main[/\\]index\.cjs/, /main[/\\]index\.(cjs|mjs|js)/,
/preload[/\\]index\.cjs/, /preload[/\\]index\.(cjs|mjs|js)/,
/renderer[/\\]index\.htm(l)?/, /renderer[/\\]index\.htm(l)?/,
], ],
}, },

View File

@@ -95,7 +95,7 @@ function getLines(code: string): string[] {
/* /*
Merges inline here-strings to a single lined string with Windows line terminator (\r\n) Merges inline here-strings to a single lined string with Windows line terminator (\r\n)
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules#here-strings https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7.4#here-strings
*/ */
function mergeHereStrings(code: string) { function mergeHereStrings(code: string) {
const regex = /@(['"])\s*(?:\r\n|\r|\n)((.|\n|\r)+?)(\r\n|\r|\n)\1@/g; const regex = /@(['"])\s*(?:\r\n|\r|\n)((.|\n|\r)+?)(\r\n|\r|\n)\1@/g;

View File

@@ -74,7 +74,7 @@ actions:
- [tcsh source code](https://web.archive.org/web/20221029212024/https://github.com/tcsh-org/tcsh). - [tcsh source code](https://web.archive.org/web/20221029212024/https://github.com/tcsh-org/tcsh).
[1]: https://web.archive.org/web/20221029134950/https://linux.die.net/man/1/tcsh "tcsh(1) - Linux man page | linux.die.net" [1]: https://web.archive.org/web/20221029134950/https://linux.die.net/man/1/tcsh "tcsh(1) - Linux man page | linux.die.net"
[2]: https://web.archive.org/web/20221029135041/https://books.google.com/books?id=LyDP5b2xzaMC&pg=PA56 "Sams Teach Yourself FreeBSD in 24 Hours - Michael Urban, Brian Tiemann - Google Books | books.google.com" [2]: https://web.archive.org/web/20221029135007/https://books.google.com/books?id=LyDP5b2xzaMC&pg=PA56#v=onepage&q&f=false "Sams Teach Yourself FreeBSD in 24 Hours - Michael Urban, Brian Tiemann - Google Books | books.google.com"
call: call:
function: DeleteFileFromUserAndRootHome function: DeleteFileFromUserAndRootHome
parameters: parameters:
@@ -1733,7 +1733,7 @@ actions:
See also: See also:
- [Source code for the Ubuntu Report tool | github.com](https://web.archive.org/web/20221029221854/https://github.com/ubuntu/ubuntu-report/) - [Source code for the Ubuntu Report tool | github.com](https://web.archive.org/web/20221029221854/https://github.com/ubuntu/ubuntu-report/)
- [Statistics gathered and visualized | ubuntu.com/desktop/statistics](https://web.archive.org/web/20221029221910/https://ubuntu.com/desktop/statistics) - [Statistics gathered and visualized | ubuntu.com/desktop/statistics](https://web.archive.org/web/20221029221910/https://ubuntu.com/desktop/statistics)
- [ubuntu-devel mailing list thread where ubuntu-report was first proposed, | lists.ubuntu.com ](https://web.archive.org/web/20221029221924/https://lists.ubuntu.com/archives/ubuntu-devel/2018-February/040139.html) - [ubuntu-devel mailing list thread where ubuntu-report was first proposed | lists.ubuntu.com](https://web.archive.org/web/20221029162523/https://lists.ubuntu.com/archives/ubuntu-devel/2018-February/040139.html)
[1]: https://web.archive.org/web/20221029162505/https://github.com/ubuntu/ubuntu-report/blob/30e902ebc17e4e10d83392d7cd3dc05fc9e35cc4/README.md "ubuntu-report/README.md at master · ubuntu/ubuntu-report | github.com" [1]: https://web.archive.org/web/20221029162505/https://github.com/ubuntu/ubuntu-report/blob/30e902ebc17e4e10d83392d7cd3dc05fc9e35cc4/README.md "ubuntu-report/README.md at master · ubuntu/ubuntu-report | github.com"
[2]: https://web.archive.org/web/20221029162538/https://github.com/ubuntu/ubuntu-report/blob/8e6030ff9bbeacacf41a9b58ea638a5c9a6f864d/README.md "More diagnostics data from desktop | lists.ubuntu.com" [2]: https://web.archive.org/web/20221029162538/https://github.com/ubuntu/ubuntu-report/blob/8e6030ff9bbeacacf41a9b58ea638a5c9a6f864d/README.md "More diagnostics data from desktop | lists.ubuntu.com"
@@ -1974,10 +1974,10 @@ actions:
Read more about Zeitgeist: Read more about Zeitgeist:
- [Official website | zeitgeist.freedesktop.org](https://web.archive.org/web/20221029222739/https://zeitgeist.freedesktop.org/) - [Official website | zeitgeist.freedesktop.org](https://web.archive.org/web/20221029150843/https://zeitgeist.freedesktop.org/)
- [Wikipedia article | en.wikipedia.org](https://web.archive.org/web/20221029222921/https://en.wikipedia.org/wiki/Zeitgeist_%28free_software%29) - [Wikipedia article | en.wikipedia.org](https://web.archive.org/web/20221029222921/https://en.wikipedia.org/wiki/Zeitgeist_%28free_software%29)
- [Launchpad project page | launchpad.net](https://web.archive.org/web/20221029223026/https://launchpad.net/zeitgeist/) - [Launchpad project page | launchpad.net](https://web.archive.org/web/20221029223026/https://launchpad.net/zeitgeist/)
- [ArchWiki article | wiki.archlinux.org](https://web.archive.org/web/20221029223033/https://wiki.archlinux.org/title/Zeitgeist) - [ArchWiki article | wiki.archlinux.org](https://web.archive.org/web/20221029164539/https://wiki.archlinux.org/title/Zeitgeist)
[1]: https://web.archive.org/web/20221029163704/https://packages.debian.org/en/sid/libdevel/libzeitgeist-2.0-dev "libzeitgeist-2.0-dev | Debian Packages | packages.debian.org" [1]: https://web.archive.org/web/20221029163704/https://packages.debian.org/en/sid/libdevel/libzeitgeist-2.0-dev "libzeitgeist-2.0-dev | Debian Packages | packages.debian.org"
[2]: https://web.archive.org/web/20221029163817/https://gitlab.gnome.org/crvi/gnome-activity-journal "crvi / GNOME Activity Journal · GitLab | gitlab.gnome.org" [2]: https://web.archive.org/web/20221029163817/https://gitlab.gnome.org/crvi/gnome-activity-journal "crvi / GNOME Activity Journal · GitLab | gitlab.gnome.org"
@@ -2116,7 +2116,7 @@ actions:
[3]: https://web.archive.org/web/20221029170026/https://packages.ubuntu.com/bionic/all/network-manager-config-connectivity-ubuntu/filelist "Ubuntu - File list of package network-manager-config-connectivity-ubuntu/bionic/all | packages.ubuntu.com" [3]: https://web.archive.org/web/20221029170026/https://packages.ubuntu.com/bionic/all/network-manager-config-connectivity-ubuntu/filelist "Ubuntu - File list of package network-manager-config-connectivity-ubuntu/bionic/all | packages.ubuntu.com"
[4]: https://web.archive.org/web/20221029170108/https://github.com/pop-os/connectivity/blob/master/debian/20-connectivity-pop.conf "connectivity/20-connectivity-pop.conf at master · pop-os/connectivity | github.com" [4]: https://web.archive.org/web/20221029170108/https://github.com/pop-os/connectivity/blob/master/debian/20-connectivity-pop.conf "connectivity/20-connectivity-pop.conf at master · pop-os/connectivity | github.com"
[5]: https://web.archive.org/web/20221029170202/https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/contrib/fedora/rpm/20-connectivity-fedora.conf "20-connectivity-fedora.conf\rpm\fedora\contrib - NetworkManager/NetworkManager - Network connection manager and user applications | reedesktop.org" [5]: https://web.archive.org/web/20221029170202/https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/contrib/fedora/rpm/20-connectivity-fedora.conf "20-connectivity-fedora.conf\rpm\fedora\contrib - NetworkManager/NetworkManager - Network connection manager and user applications | reedesktop.org"
[6]: https://web.archive.org/web/20221029170207/https://fedora.pkgs.org/35/fedora-updates-testing-x86_64/NetworkManager-config-connectivity-fedora-1.32.12-1.fc35.noarch.rpm.html "NetworkManager-config-connectivity-fedora | fedora.pkgs.org" [6]: https://archive.ph/2023.12.06-185917/https://pkgs.org/download/NetworkManager-config-connectivity-fedora "Networkmanager-config-connectivity-fedora Download (RPM) | pkgs.org"
call: call:
function: RunIfCommandExists function: RunIfCommandExists
parameters: parameters:
@@ -2202,7 +2202,7 @@ actions:
- Diagnostic information about your system and usage is sent to Microsoft servers [3]. - Diagnostic information about your system and usage is sent to Microsoft servers [3].
- Your usage data and data about feature performance [3]. - Your usage data and data about feature performance [3].
[1]: https://web.archive.org/web/20221029170818/https://en.wikipedia.org/wiki/Visual_Studio_Code "Visual Studio Code - Wikipedia | en.wikipedia.org" [1]: https://web.archive.org/web/20221029142001/https://en.wikipedia.org/wiki/Visual_Studio_Code "Visual Studio Code - Wikipedia | en.wikipedia.org"
[2]: https://web.archive.org/web/20221029170840/https://code.visualstudio.com/updates/v1_26#_offline-mode "Visual Studio Code July 2018 | code.visualstudio.com" [2]: https://web.archive.org/web/20221029170840/https://code.visualstudio.com/updates/v1_26#_offline-mode "Visual Studio Code July 2018 | code.visualstudio.com"
[3]: https://web.archive.org/web/20221029171138/https://code.visualstudio.com/docs/getstarted/telemetry "Visual Studio Code Telemetry | code.visualstudio.com" [3]: https://web.archive.org/web/20221029171138/https://code.visualstudio.com/docs/getstarted/telemetry "Visual Studio Code Telemetry | code.visualstudio.com"
children: children:
@@ -2697,7 +2697,7 @@ actions:
[2]: https://web.archive.org/web/20231003094154/https://bugzilla.mozilla.org/show_bug.cgi?id=1746646 "1746646 - (tcp-mochitests) [meta] Make mochitests work with TCP enabled (cookieBehavior = 5) | bugzilla.mozilla.org" [2]: https://web.archive.org/web/20231003094154/https://bugzilla.mozilla.org/show_bug.cgi?id=1746646 "1746646 - (tcp-mochitests) [meta] Make mochitests work with TCP enabled (cookieBehavior = 5) | bugzilla.mozilla.org"
[3]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning#disable_dynamic_state_partitioning "State Partitioning - Privacy on the web | MDN" [3]: https://web.archive.org/web/20230918172155/https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning#disable_dynamic_state_partitioning "State Partitioning - Privacy on the web | MDN"
[4]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c5 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org" [4]: https://web.archive.org/web/20231003094207/https://bugzilla.mozilla.org/show_bug.cgi?id=1649876#c5 "1649876 - Migrate FPI users to dFPI | bugzilla.mozilla.org"
[5]: https://blog.mozilla.org/en/products/firefox/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/ "Firefox Rolls Out Total Cookie Protection By Default" [5]: https://web.archive.org/web/20231207105610/https://blog.mozilla.org/en/products/firefox/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/ "Firefox Rolls Out Total Cookie Protection By Default"
[6]: https://web.archive.org/web/20231003094350/https://bugzilla.mozilla.org/show_bug.cgi?id=1631676#c25 "1631676 - Disable dfpi when privacy.firstparty.isolate=true | bugzilla.mozilla.org" [6]: https://web.archive.org/web/20231003094350/https://bugzilla.mozilla.org/show_bug.cgi?id=1631676#c25 "1631676 - Disable dfpi when privacy.firstparty.isolate=true | bugzilla.mozilla.org"
call: call:
function: AddFirefoxPrefs function: AddFirefoxPrefs
@@ -2887,7 +2887,7 @@ actions:
setting [4]. setting [4].
[1]: https://web.archive.org/web/20221015102124/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html "Preferences and Defines — Firefox Source Docs documentation | firefox-source-docs.mozilla.org" [1]: https://web.archive.org/web/20221015102124/https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html "Preferences and Defines — Firefox Source Docs documentation | firefox-source-docs.mozilla.org"
[2]: https://web.archive.org/web/20221015102305/https://searchfox.org/mozilla-central/source/modules/libpref/Preferences.cpp#3213 [2]: https://web.archive.org/web/20221015102338/https://searchfox.org/mozilla-central/source/modules/libpref/Preferences.cpp#3213
[3]: https://web.archive.org/web/20221015102419/https://bugzilla.mozilla.org/show_bug.cgi?id=1422689#c1 [3]: https://web.archive.org/web/20221015102419/https://bugzilla.mozilla.org/show_bug.cgi?id=1422689#c1
[4]: https://web.archive.org/web/20221015102604/https://stigviewer.com/stig/mozilla_firefox/2020-12-10/finding/V-223170 [4]: https://web.archive.org/web/20221015102604/https://stigviewer.com/stig/mozilla_firefox/2020-12-10/finding/V-223170
call: call:
@@ -3173,7 +3173,7 @@ actions:
portal is in place and blocking traffic, this feature prevents all other connection attempts, portal is in place and blocking traffic, this feature prevents all other connection attempts,
possibly revealing your usage habits. possibly revealing your usage habits.
See also: [Captive portal | Wikipedia](https://web.archive.org/web/20221029223534/https://en.wikipedia.org/wiki/Captive_portal). See also: [Captive portal | Wikipedia](https://web.archive.org/web/20221029163002/https://en.wikipedia.org/wiki/Captive_portal).
This script sets `network.captive-portal-service.enabled` to 'false', thereby disabling automatic This script sets `network.captive-portal-service.enabled` to 'false', thereby disabling automatic
connections [1]. connections [1].
@@ -3207,7 +3207,7 @@ actions:
There have been concerns about the potential for Google Safe Browsing to be used for censorship There have been concerns about the potential for Google Safe Browsing to be used for censorship
in the future, although this has not occurred as of yet [3]. in the future, although this has not occurred as of yet [3].
[1]: https://web.archive.org/web/20221025192643/https://wiki.mozilla.org/Security/Safe_Browsing "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org" [1]: https://web.archive.org/web/20221026164502/https://wiki.mozilla.org/Security/Safe_Browsing "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org"
[2]: https://web.archive.org/web/20221025193000/https://support.mozilla.org/en-US/kb/how-does-phishing-and-malware-protection-work#w_what-information-is-sent-to-mozilla-or-its-partners-when-phishing-and-malware-protection-is-enabled [2]: https://web.archive.org/web/20221025193000/https://support.mozilla.org/en-US/kb/how-does-phishing-and-malware-protection-work#w_what-information-is-sent-to-mozilla-or-its-partners-when-phishing-and-malware-protection-is-enabled
[3]: https://web.archive.org/web/20221025192516/https://www.usnews.com/opinion/articles/2016-06-22/google-is-the-worlds-biggest-censor-and-its-power-must-be-regulated "Google Is the World's Biggest Censor and Its Power Must Be Regulated | usnews.com" [3]: https://web.archive.org/web/20221025192516/https://www.usnews.com/opinion/articles/2016-06-22/google-is-the-worlds-biggest-censor-and-its-power-must-be-regulated "Google Is the World's Biggest Censor and Its Power Must Be Regulated | usnews.com"
children: children:
@@ -3226,7 +3226,7 @@ actions:
If this blocking is removed, the user should be knowledgeable about the potential risks and will take precautions. If this blocking is removed, the user should be knowledgeable about the potential risks and will take precautions.
[1]: https://web.archive.org/web/20221025192643/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org" [1]: https://web.archive.org/web/20221026164502/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org"
[2]: https://web.archive.org/web/20230811024650/https://blog.mozilla.org/addons/2020/08/24/introducing-a-scalable-add-ons-blocklist/ "Introducing a scalable add-ons blocklist | Mozilla Add-ons Community Blog" [2]: https://web.archive.org/web/20230811024650/https://blog.mozilla.org/addons/2020/08/24/introducing-a-scalable-add-ons-blocklist/ "Introducing a scalable add-ons blocklist | Mozilla Add-ons Community Blog"
call: call:
function: AddFirefoxPrefs function: AddFirefoxPrefs
@@ -3286,7 +3286,7 @@ actions:
It is active by default [2]. It is active by default [2].
[1]: https://web.archive.org/web/20221025192643/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org" [1]: https://web.archive.org/web/20221026164502/https://wiki.mozilla.org/Security/Safe_Browsing#Prefs "Security/Safe Browsing - MozillaWiki | wiki.mozilla.org"
[2]: https://web.archive.org/web/20221029173442/https://github.com/mozilla/policy-templates/blob/master/README.md#preferences "policy-templates/README.md at master · mozilla/policy-templates · GitHub | github.com" [2]: https://web.archive.org/web/20221029173442/https://github.com/mozilla/policy-templates/blob/master/README.md#preferences "policy-templates/README.md at master · mozilla/policy-templates · GitHub | github.com"
call: call:
function: AddFirefoxPrefs function: AddFirefoxPrefs

View File

@@ -108,7 +108,7 @@ actions:
name: Clear user activity audit logs (login, logout, authentication, etc.) name: Clear user activity audit logs (login, logout, authentication, etc.)
docs: docs:
- https://papers.put.as/papers/macosx/2012/Mac_Log_Analysis_Sarah_Edwards_DFIRSummit2012.pdf - https://papers.put.as/papers/macosx/2012/Mac_Log_Analysis_Sarah_Edwards_DFIRSummit2012.pdf
- http://macadmins.psu.edu/wp-content/uploads/sites/24696/2016/06/psumac2016-19-osxlogs_macadmins_2016.pdf - https://web.archive.org/web/20240314054514/https://bpb-us-e1.wpmucdn.com/sites.psu.edu/dist/4/24696/files/2016/06/psumac2016-19-osxlogs_macadmins_2016.pdf
code: |- code: |-
sudo rm -rfv /var/audit/* sudo rm -rfv /var/audit/*
sudo rm -rfv /private/var/audit/* sudo rm -rfv /private/var/audit/*
@@ -171,7 +171,7 @@ actions:
- -
name: Clear Safari last session (open tabs) history name: Clear Safari last session (open tabs) history
docs: docs:
- https://apple.stackexchange.com/a/374116 - https://web.archive.org/web/20240314061752/https://apple.stackexchange.com/questions/374099/where-does-safari-store-the-open-tabs/374116#374116
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-7127 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-7127
code: rm -f ~/Library/Safari/LastSession.plist code: rm -f ~/Library/Safari/LastSession.plist
- -
@@ -191,7 +191,7 @@ actions:
name: Clear Safari webpage previews (thumbnails) name: Clear Safari webpage previews (thumbnails)
docs: docs:
- https://davidkoepi.wordpress.com/2013/04/20/safariforensic/ - https://davidkoepi.wordpress.com/2013/04/20/safariforensic/
- https://www.reddit.com/r/apple/comments/18lp92/your_apple_computer_keeps_a_screen_shot_of_nearly/ - https://archive.ph/2024.03.14-100910/https://www.reddit.com/r/apple/comments/18lp92/your_apple_computer_keeps_a_screen_shot_of_nearly/?rdt=59921
code: rm -rfv ~/Library/Caches/com.apple.Safari/Webpage\ Previews code: rm -rfv ~/Library/Caches/com.apple.Safari/Webpage\ Previews
- -
name: Clear Safari history copy name: Clear Safari history copy
@@ -204,8 +204,8 @@ actions:
- -
name: Clear Safari cookies name: Clear Safari cookies
docs: docs:
- https://www.toolbox.com/tech/operating-systems/blogs/understanding-the-safari-cookiesbinarycookies-file-format-010712/ - https://web.archive.org/web/20240314132018/https://community.spiceworks.com/t/understanding-the-safari-cookies-binarycookies-file-format/928827
- https://link.springer.com/content/pdf/10.1007/0-387-36891-4_13.pdf - https://web.archive.org/web/20240314060318/https://link.springer.com/content/pdf/10.1007/0-387-36891-4_13.pdf
code: |- code: |-
rm -f ~/Library/Cookies/Cookies.binarycookies rm -f ~/Library/Cookies/Cookies.binarycookies
# Used before Safari 5.1 # Used before Safari 5.1
@@ -520,7 +520,7 @@ actions:
you'll be prompted to grant or deny permission. It's a proactive step to ensure that your sensitive information you'll be prompted to grant or deny permission. It's a proactive step to ensure that your sensitive information
or system services are accessed only with your current and informed consent. or system services are accessed only with your current and informed consent.
children: children:
# Main documentation: https://archive.ph/26Hlq (https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services) # Main documentation: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services
- -
name: Clear **"All"** permissions name: Clear **"All"** permissions
docs: |- docs: |-
@@ -536,7 +536,7 @@ actions:
This script resets permissions for camera access [1]. This script resets permissions for camera access [1].
It ensures no application can access the system camera without explicit user permission, protecting against unauthorized surveillance and data breaches. It ensures no application can access the system camera without explicit user permission, protecting against unauthorized surveillance and data breaches.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -547,7 +547,7 @@ actions:
This script resets permissions for microphone access [1]. This script resets permissions for microphone access [1].
It revokes all granted access to the microphone, protecting against eavesdropping and unauthorized audio recording by applications. It revokes all granted access to the microphone, protecting against eavesdropping and unauthorized audio recording by applications.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -558,7 +558,7 @@ actions:
This script resets permissions for accessibility features [1]. This script resets permissions for accessibility features [1].
It revokes application access to accessibility services, preventing misuse and ensuring these features are used only with user consent. It revokes application access to accessibility services, preventing misuse and ensuring these features are used only with user consent.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -569,7 +569,7 @@ actions:
This script resets permissions for screen capture [1]. This script resets permissions for screen capture [1].
It ensures applications cannot capture screen content without user authorization, protecting sensitive information displayed on the screen. It ensures applications cannot capture screen content without user authorization, protecting sensitive information displayed on the screen.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -580,7 +580,7 @@ actions:
This script resets permissions for accessing reminders information managed by the Reminders app [1]. This script resets permissions for accessing reminders information managed by the Reminders app [1].
It ensures applications cannot access or modify reminders data without explicit user permission, maintaining the privacy of personal reminders. It ensures applications cannot access or modify reminders data without explicit user permission, maintaining the privacy of personal reminders.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -591,7 +591,7 @@ actions:
This script resets permissions for accessing the pictures managed by the Photos app [1]. This script resets permissions for accessing the pictures managed by the Photos app [1].
It revokes all permissions granted to applications, safeguarding personal photos and media from unauthorized access. It revokes all permissions granted to applications, safeguarding personal photos and media from unauthorized access.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -602,7 +602,7 @@ actions:
This script resets permissions for accessing the calendar information managed by the Calendar app [1]. This script resets permissions for accessing the calendar information managed by the Calendar app [1].
It ensures that applications cannot access calendar data without user consent, protecting personal and sensitive calendar information. It ensures that applications cannot access calendar data without user consent, protecting personal and sensitive calendar information.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -614,7 +614,7 @@ actions:
Full disk access allows the application access to all protected files, including system administration files [1]. Full disk access allows the application access to all protected files, including system administration files [1].
It revokes broad file access from applications, significantly reducing the risk of data exposure and enhancing overall system security. It revokes broad file access from applications, significantly reducing the risk of data exposure and enhancing overall system security.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -626,7 +626,7 @@ actions:
The contact information managed by the Contacts app [1]. The contact information managed by the Contacts app [1].
It ensures that applications cannot access the user's contact list without explicit permission, maintaining the confidentiality of personal contacts. It ensures that applications cannot access the user's contact list without explicit permission, maintaining the confidentiality of personal contacts.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -637,7 +637,7 @@ actions:
This script resets permissions for accessing the Desktop folder [1]. This script resets permissions for accessing the Desktop folder [1].
It revokes application access to files on the desktop, protecting personal and work-related documents from unauthorized access. It revokes application access to files on the desktop, protecting personal and work-related documents from unauthorized access.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -648,7 +648,7 @@ actions:
This script resets permissions for accessing the Documents folder [1]. This script resets permissions for accessing the Documents folder [1].
It prevents applications from accessing files in this folder without user consent, safeguarding important and private documents. It prevents applications from accessing files in this folder without user consent, safeguarding important and private documents.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -659,7 +659,7 @@ actions:
This script resets permissions for accessing the Downloads folder [1]. This script resets permissions for accessing the Downloads folder [1].
It ensures that applications cannot access downloaded files without user authorization, protecting downloaded content from misuse. It ensures that applications cannot access downloaded files without user authorization, protecting downloaded content from misuse.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -670,7 +670,7 @@ actions:
This script resets permissions for Apple Events [1]. This script resets permissions for Apple Events [1].
It revokes permissions for applications to send restricted Apple Events to other processes [1], enhancing privacy and security. It revokes permissions for applications to send restricted Apple Events to other processes [1], enhancing privacy and security.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -681,7 +681,7 @@ actions:
This script resets permissions for File Provider Presence [1]. This script resets permissions for File Provider Presence [1].
It revokes the ability of File Provider applications to know when the user is accessing their managed files [1], enhancing user privacy. It revokes the ability of File Provider applications to know when the user is accessing their managed files [1], enhancing user privacy.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -692,7 +692,7 @@ actions:
This script resets "ListenEvent" permissions [1]. This script resets "ListenEvent" permissions [1].
It revokes application access to listen to system events [1], preventing unauthorized monitoring of user interactions with the system. It revokes application access to listen to system events [1], preventing unauthorized monitoring of user interactions with the system.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -703,7 +703,7 @@ actions:
This script resets permissions for accessing the Media Library [1]. This script resets permissions for accessing the Media Library [1].
It ensures that applications cannot access Apple Music, music and video activity, and the media library [1] without user consent. It ensures that applications cannot access Apple Music, music and video activity, and the media library [1] without user consent.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -714,7 +714,7 @@ actions:
This script resets permissions for sending "PostEvent" [1]. This script resets permissions for sending "PostEvent" [1].
It prevents applications from using CoreGraphics APIs to send system events [1], safeguarding against potential misuse. It prevents applications from using CoreGraphics APIs to send system events [1], safeguarding against potential misuse.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -726,7 +726,7 @@ actions:
This script resets permissions for using Speech Recognition [1]. This script resets permissions for using Speech Recognition [1].
It revokes application access to the speech recognition facility and sending speech data to Apple [1], protecting user privacy. It revokes application access to the speech recognition facility and sending speech data to Apple [1], protecting user privacy.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -737,7 +737,7 @@ actions:
This script resets permissions for modifying other apps [1]. This script resets permissions for modifying other apps [1].
It prevents applications from updating or deleting other apps [1], maintaining system integrity and user control. It prevents applications from updating or deleting other apps [1], maintaining system integrity and user control.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -748,7 +748,7 @@ actions:
This script resets permissions for accessing application data [1]. This script resets permissions for accessing application data [1].
It revokes application access to specific application data, enhancing privacy and data security. It revokes application access to specific application data, enhancing privacy and data security.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -759,7 +759,7 @@ actions:
This script resets permissions for accessing files on network volumes [1]. This script resets permissions for accessing files on network volumes [1].
It ensures applications cannot access network files without user authorization. It ensures applications cannot access network files without user authorization.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -770,7 +770,7 @@ actions:
This script resets permissions for accessing files on removable volumes [1]. This script resets permissions for accessing files on removable volumes [1].
It protects data on external drives from unauthorized application access. It protects data on external drives from unauthorized application access.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -781,7 +781,7 @@ actions:
This script resets permissions for accessing system administration files [1]. This script resets permissions for accessing system administration files [1].
It enhances system security by restricting application access to critical system files. It enhances system security by restricting application access to critical system files.
[1]: https://archive.ph/26Hlq "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com" [1]: https://archive.ph/2023.11.24-170934/https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol/services "PrivacyPreferencesPolicyControl.Services | Apple Developer Documentation | apple.com"
call: call:
function: ResetServicePermissions function: ResetServicePermissions
parameters: parameters:
@@ -877,7 +877,7 @@ actions:
There is also `WelcomeScreenPromo.PromoOff` setting that's pre-configured to `1` (`no` as There is also `WelcomeScreenPromo.PromoOff` setting that's pre-configured to `1` (`no` as
default). It's undocumented but still kept disabled by this script. default). It's undocumented but still kept disabled by this script.
[1]: https://web.archive.org/save/https://forum.parallels.com/threads/unable-to-process-the-upgrade-request.345603/ "Unable to process the upgrade request | Parallels Forums | forum.parallels.com" [1]: https://web.archive.org/web/20240314062932/https://forum.parallels.com/threads/unable-to-process-the-upgrade-request.345603/ "Unable to process the upgrade request | Parallels Forums | forum.parallels.com"
[2]: https://web.archive.org/web/20221012151800/https://kb.parallels.com/114422 "How do I turn off notifications in Parallels Desktop and Parallels Access? | Knowledge Base | parallels.com" [2]: https://web.archive.org/web/20221012151800/https://kb.parallels.com/114422 "How do I turn off notifications in Parallels Desktop and Parallels Access? | Knowledge Base | parallels.com"
code: |- code: |-
defaults write 'com.parallels.Parallels Desktop' 'ProductPromo.ForcePromoOff' -bool yes defaults write 'com.parallels.Parallels Desktop' 'ProductPromo.ForcePromoOff' -bool yes
@@ -988,16 +988,16 @@ actions:
recommend: strict recommend: strict
docs: docs:
- https://github.com/privacysexy-forks/starter/blob/master/system/siri.sh - https://github.com/privacysexy-forks/starter/blob/master/system/siri.sh
- https://machippie.github.io/system/ - https://web.archive.org/web/20201002133713/https://machippie.github.io/system/
code: defaults write com.apple.assistant.backedup 'Use device speaker for TTS' -int 3 code: defaults write com.apple.assistant.backedup 'Use device speaker for TTS' -int 3
revertCode: defaults write com.apple.assistant.backedup 'Use device speaker for TTS' -int 2 revertCode: defaults write com.apple.assistant.backedup 'Use device speaker for TTS' -int 2
- -
name: Disable Siri services (Siri and assistantd) name: Disable Siri services (Siri and assistantd)
recommend: strict recommend: strict
docs: docs:
- https://apple.stackexchange.com/questions/57514/what-is-assistantd - https://web.archive.org/web/20240314060540/https://apple.stackexchange.com/questions/57514/what-is-assistantd
- https://www.jamf.com/jamf-nation/discussions/22757/kill-siri#responseChild137563 - https://archive.ph/2024.03.14-055010/https://community.jamf.com/t5/jamf-pro/kill-siri/td-p/171543
- https://apple.stackexchange.com/a/370426 - https://web.archive.org/web/20240314060501/https://apple.stackexchange.com/questions/258816/how-to-completely-disable-siri-on-sierra/370426#370426
# To see status: • `launchctl print-disabled system` • `launchctl print-disabled user/$UID` • `launchctl print-disabled gui/$UID` # To see status: • `launchctl print-disabled system` • `launchctl print-disabled user/$UID` • `launchctl print-disabled gui/$UID`
code: |- code: |-
launchctl disable "user/$UID/com.apple.assistantd" launchctl disable "user/$UID/com.apple.assistantd"
@@ -1021,10 +1021,20 @@ actions:
fi fi
- -
name: Disable "Do you want to enable Siri?" pop-up name: Disable "Do you want to enable Siri?" pop-up
docs: docs: |-
- https://discussions.apple.com/thread/7694127?answerId=30752577022#30752577022 This script stops the "Enable Siri" pop-up [1] from appearing the first time a user logs into macOS [2].
- https://windowsreport.com/mac/siri-keeps-popping-up/
- https://www.jamf.com/jamf-nation/discussions/21783/disable-siri-setup-assistant-in-macos-sierra#responseChild131588 Introduced in macOS version 10.12 [2], this pop-up asks, "Do you want to enable Siri?" [1]
which could lead to Siri being enabled unintentionally.
This script configures the `com.apple.SetupAssistant!DidSeeSiriSetup` setting to suppress this pop-up [1] [2] [3] [4].
This command tells the system that the Siri setup is complete, preventing the pop-up in future sessions and
enhancing privacy by avoiding unintended Siri activation.
[1]: https://archive.ph/2024.03.14-053325/https://discussions.apple.com/thread/7694127?answerId=30752577022&sortBy=best%2330752577022 "macOS keeps nagging me about enabling Siri - Apple Community | discussions.apple.com"
[2]: https://web.archive.org/web/20240314052600/https://derflounder.wordpress.com/2016/09/20/supressing-siri-pop-up-windows-on-macos-sierra/ "Suppressing Siri pop-up windows on macOS Sierra | Der Flounder"
[3]: https://web.archive.org/web/20240314052901/https://windowsreport.com/mac/siri-keeps-popping-up/ "Siri keeps popping up on Mac? Here's how to easily fix that • MacTips | windowsreport.com"
[4]: https://web.archive.org/web/20240314052247/https://community.jamf.com/t5/jamf-pro/disable-siri-setup-assistant-in-macos-sierra/m-p/205836/highlight/true#M194536 "Solved: Re: Disable Siri setup assistant in macOS Sierra - Jamf Nation Community - 205834 | community.jamf.com"
code: defaults write com.apple.SetupAssistant 'DidSeeSiriSetup' -bool True code: defaults write com.apple.SetupAssistant 'DidSeeSiriSetup' -bool True
revertCode: defaults delete com.apple.SetupAssistant 'DidSeeSiriSetup' revertCode: defaults delete com.apple.SetupAssistant 'DidSeeSiriSetup'
- -
@@ -1084,7 +1094,7 @@ actions:
by default. by default.
[1]: https://web.archive.org/web/20230731152633/https://www.apple.com/legal/privacy/data/en/apple-advertising/ "Legal - Apple Advertising & Privacy - Apple" [1]: https://web.archive.org/web/20230731152633/https://www.apple.com/legal/privacy/data/en/apple-advertising/ "Legal - Apple Advertising & Privacy - Apple"
[2]: https://web.archive.org/web/20220805052411/https://support.apple.com/en-sg/guide/mac-help/mh32356/mac: "Change Privacy preferences on Mac - Apple Support (SG)" [2]: https://web.archive.org/web/20220805052411/https://support.apple.com/en-sg/guide/mac-help/mh32356/mac "Change Privacy preferences on Mac - Apple Support (SG)"
[3]: https://web.archive.org/web/20230731155827/https://developer.apple.com/documentation/devicemanagement/restrictions "Restrictions | Apple Developer Documentation" [3]: https://web.archive.org/web/20230731155827/https://developer.apple.com/documentation/devicemanagement/restrictions "Restrictions | Apple Developer Documentation"
[4]: https://web.archive.org/web/20230731155653/https://paper.bobylive.com/Security/CIS/CIS_Apple_macOS_11_0_Big_Sur_Benchmark_v2_0_0.pdf "CIS Apple macOS 11.0 Big Sur Benchmark" [4]: https://web.archive.org/web/20230731155653/https://paper.bobylive.com/Security/CIS/CIS_Apple_macOS_11_0_Big_Sur_Benchmark_v2_0_0.pdf "CIS Apple macOS 11.0 Big Sur Benchmark"
[5]: https://web.archive.org/web/20230731155131/https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614151-advertisingidentifier "advertisingIdentifier | Apple Developer Documentation" [5]: https://web.archive.org/web/20230731155131/https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614151-advertisingidentifier "advertisingIdentifier | Apple Developer Documentation"
@@ -1280,7 +1290,7 @@ actions:
# OS tracks downloaded files with help of quarantine-aware applications # OS tracks downloaded files with help of quarantine-aware applications
# (such as Safari, Chrome) adding quarantine extended attributes to files. # (such as Safari, Chrome) adding quarantine extended attributes to files.
# then OS warns and asks if you really want to open it # then OS warns and asks if you really want to open it
docs: https://support.apple.com/en-gb/HT202491 docs: https://web.archive.org/web/20210319081714/https://support.apple.com/en-gb/HT202491
children: children:
- -
category: Clean File Quarantine from downloaded files category: Clean File Quarantine from downloaded files
@@ -1391,7 +1401,7 @@ actions:
name: Disable Gatekeeper's automatic reactivation name: Disable Gatekeeper's automatic reactivation
docs: docs:
- https://osxdaily.com/2015/11/05/stop-gatekeeper-auto-rearm-mac-os-x/ - https://osxdaily.com/2015/11/05/stop-gatekeeper-auto-rearm-mac-os-x/
- https://www.cnet.com/tech/computing/how-to-disable-gatekeeper-permanently-on-os-x/ - https://web.archive.org/web/20230327050142/https://www.cnet.com/tech/computing/how-to-disable-gatekeeper-permanently-on-os-x/
code: sudo defaults write /Library/Preferences/com.apple.security GKAutoRearm -bool true code: sudo defaults write /Library/Preferences/com.apple.security GKAutoRearm -bool true
revertCode: sudo defaults write /Library/Preferences/com.apple.security GKAutoRearm -bool false revertCode: sudo defaults write /Library/Preferences/com.apple.security GKAutoRearm -bool false
- -
@@ -1450,13 +1460,19 @@ actions:
revertCode: sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist 'DisableLibraryValidation' -bool false revertCode: sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist 'DisableLibraryValidation' -bool false
- -
category: Disable automatic updates category: Disable automatic updates
docs: docs: |-
- https://developer.apple.com/documentation/devicemanagement/deviceinformationresponse/queryresponses/osupdatesettings This category contains scripts to disable automatic operating system updates.
- https://macadminsdoc.readthedocs.io/en/master/Profiles-and-Settings/OS-X-Updates.html
Disabling automatic updates gives users full control over when and which updates are applied to their system.
It improves privacy by preventing unwanted data collection, new vulnerabilities and unapproved changes to system settings.
> **Caution**:
> Disabling automatic updates can leave your system vulnerable to unpatched exploits.
> Manually check and and apply updates to stay protected.
children: children:
- -
name: Disable automatic checks for updates name: Disable automatic checks for updates
docs: https://developer.apple.com/documentation/devicemanagement/softwareupdate docs: https://archive.ph/2024.03.21-180353/https://developer.apple.com/documentation/devicemanagement/softwareupdate
code: |- code: |-
# For OS X Yosemite and newer (>= 10.10) # For OS X Yosemite and newer (>= 10.10)
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticCheckEnabled' -bool false sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticCheckEnabled' -bool false
@@ -1465,7 +1481,7 @@ actions:
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticCheckEnabled' -bool true sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticCheckEnabled' -bool true
- -
name: Disable automatic downloads for updates name: Disable automatic downloads for updates
docs: https://developer.apple.com/documentation/devicemanagement/softwareupdate docs: https://archive.ph/2024.03.21-180353/https://developer.apple.com/documentation/devicemanagement/softwareupdate
code: |- code: |-
# For OS X Yosemite and newer (>= 10.10) # For OS X Yosemite and newer (>= 10.10)
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticDownload' -bool false sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticDownload' -bool false
@@ -1474,12 +1490,41 @@ actions:
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticDownload' -bool true sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticDownload' -bool true
- -
name: Disable automatic installation of macOS updates name: Disable automatic installation of macOS updates
docs: docs: |-
# References for AutoUpdateRestartRequired This script stops macOS from automatically installing updates.
- https://kb.vmware.com/s/article/2960635
- https://derflounder.wordpress.com/2018/12/28/enabling-automatic-macos-software-updates-for-os-x-yosemite-through-macos-mojave/ This script improves privacy by reducing unwanted data collection and ensuring updates don't change
# References for AutomaticallyInstallMacOSUpdates settings or data without your approval.
- https://developer.apple.com/documentation/devicemanagement/softwareupdate
The Center for Internet Security (CIS) advises against automatic updates in scenarios where changes require
thorough testing and approval processes to avoid operational disruptions [1] [2] [3] [4].
This script configures following to stop macOS from installing updates automatically:
1. `/Library/Preferences/com.apple.commerce!AutoUpdateRestartRequired`:
This preference stops the system from automatically installing macOS updates [1] [2] [3] [4] [5] [6] [7] [8].
By doing this, updates will only be installed when you decide, giving you a chance to check them first [1] [2] [3] [4] [5] [6] [7] [8].
This setting applies to OS X Yosemite through macOS High Sierra [7] [9].
2. `/Library/Preferences/com.apple.commerce!AutomaticallyInstallMacOSUpdates`:
Changing this setting stops macOS from installing updates automatically [3] [5] [9] [10], giving you control over when to update.
If restricts the *Install macOS Updates* option and prevents the user from changing the option [10].
While this setting enhances privacy, it's generally not advised by NIST due to potential security risks [9].
This setting applies to macOS Mojave and newer versions [9].
> **Caution**: Disabling automatic updates requires you to manually check and apply updates to stay protected against security threats [1] [2] [3] [4].
[1]: https://web.archive.org/web/20240321165149/https://www.tenable.com/audits/items/CIS_Apple_macOS_10.12_v1.1.0_Level_1.audit:e02dfdd6bec9556a3ce537f60b91b549 "CIS Apple macOS 10.12 L1 v1.1.0 | 1.5 Enable OS X update installs | Tenable®"
[2]: https://web.archive.org/web/20240321165851/https://paper.bobylive.com/Security/CIS/CIS_Apple_macOS_10_13_Benchmark_v1_1_0---PDF.pdf "CIS Apple macOS 10.13 Benchmark v1.1.0 | paper.bobylive.com"
[3]: https://web.archive.org/web/20240321170400/https://www.tenable.com/audits/items/CIS_Apple_macOS_13.0_Ventura_v1.0.0_L1.audit:fe03c59a39c7c949507ff20d07f89993 "1.4 Ensure Install of macOS Updates Is Enabled | Tenable® | www.tenable.com"
[4]: https://web.archive.org/web/20240321170036/https://paper.bobylive.com/Security/CIS/CIS_Apple_macOS_10_14_Benchmark_v1_4_0_PDF.pdf "CIS Apple macOS 10.14 Benchmark v1.4.0 | paper.bobylive.com"
[5]: https://web.archive.org/web/20240321164917/https://www.ncsc.gov.uk/files/macos_provisioning_script.sh_.txt "macOS provisioning script | UK National Cyber Security Centre | www.ncsc.gov.uk"
[6]: https://web.archive.org/web/20240321165118/https://macadminsdoc.readthedocs.io/en/master/Profiles-and-Settings/OS-X-Updates.html "macOS Updates — MacAdmins Community Documentation documentation | macadminsdoc.readthedocs.io"
[7]: https://web.archive.org/web/20240321165304/https://derflounder.wordpress.com/2014/12/29/managing-automatic-app-store-and-os-x-update-installation-on-yosemite/ "Managing automatic App Store and OS X update installation on Yosemite | Der Flounder | derflounder.wordpress.com"
[8]: https://web.archive.org/web/20240321170034/https://krypted.com/mac-os-x/app-store-preferences-set-server-5-4-macos-high-sierra/ "App Store Preferences To Set In On Server 5.4 for macOS High Sierra krypted | krypted.com"
[9]: https://web.archive.org/web/20240321170251/https://derflounder.wordpress.com/2018/12/28/enabling-automatic-macos-software-updates-for-os-x-yosemite-through-macos-mojave/ "Enabling automatic macOS software updates for OS X Yosemite through macOS Mojave | Der Flounder | derflounder.wordpress.com"
[10]: https://archive.ph/2024.03.21-180353/https://developer.apple.com/documentation/devicemanagement/softwareupdate "SoftwareUpdate | Apple Developer Documentation | developer.apple.com"
[11]: https://web.archive.org/web/20240321165931/https://csrc.nist.gov/CSRC/media/Projects/national-vulnerability-database/documents/CCE/CCE-macos_monterey.xls "CCE-91129-7 | CCE-macos_monterey.xls | Sheet 1 - NIST Computer Security Resource Center | csrc.nist.gov"
code: |- code: |-
# For OS X Yosemite through macOS High Sierra (>= 10.10 && < 10.14) # For OS X Yosemite through macOS High Sierra (>= 10.10 && < 10.14)
sudo defaults write /Library/Preferences/com.apple.commerce 'AutoUpdateRestartRequired' -bool false sudo defaults write /Library/Preferences/com.apple.commerce 'AutoUpdateRestartRequired' -bool false
@@ -1492,9 +1537,44 @@ actions:
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticallyInstallMacOSUpdates' -bool true sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticallyInstallMacOSUpdates' -bool true
- -
name: Disable automatic app updates from the App Store name: Disable automatic app updates from the App Store
docs: docs: |-
- https://kb.vmware.com/s/article/2960635 This script disables automatic app updates [1] [2] [3] [4] from the App Store [5] [6] [7] [8] [9] [10] [11] [12] [13].
- https://derflounder.wordpress.com/2018/12/28/enabling-automatic-macos-software-updates-for-os-x-yosemite-through-macos-mojave/ It prevents automatic installation of application updates as soon as they become available from Apple [2] [3] [6] [9] [11] [12] [13].
Thus, applications are updated only when you choose to do so [5].
Disabling automatic updates prevents unexpected app behavior or settings changes.
It helps you to maintain your current app configurations and privacy settings.
It also protects against potential zero-day vulnerabilities in your apps.
This gives you the ability to choose which updates to install and when, enabling you to review the details of updates before deciding to proceed.
The script modifies the following settings:
1. `/Library/Preferences/com.apple.commerce!AutoUpdate`:
Disables automated app updates [1] [2] [3] [6] [9] [10] [13] from the App Store [7] [8].
This setting applies to OS X Yosemite and newer versions [1].
2. `/Library/Preferences/com.apple.SoftwareUpdate!AutomaticallyInstallAppUpdates`:
Stops the automatic installation of app updates [1] [4] from App Store [9] [10] [11] [12] [13].
It deselects the *Install app updates from the App Store* option and prevents the user from changing the option [10].
While this setting enhances privacy, it's generally not advised by NIST due to potential security risks [4].
This setting applies to macOS Mojave and newer versions [1].
> **Caution**:
> Disabling app updates means you should manually check for and install important security patches for every application
> to protect against vulnerabilities [2] [3] [5] [6] [9] [11] [12] [13].
[1]: https://web.archive.org/web/20240321170251/https://derflounder.wordpress.com/2018/12/28/enabling-automatic-macos-software-updates-for-os-x-yosemite-through-macos-mojave/ "Enabling automatic macOS software updates for OS X Yosemite through macOS Mojave | Der Flounder | derflounder.wordpress.com"
[2]: https://web.archive.org/web/20240321190032/https://www.irs.gov/pub/irs-utl/safeguards-scsem-macosx-v6-1-093021.xlsx "SCSEM OSX 10.14 | Internal Revenue Service Office of Safeguards | www.irs.gov"
[3]: https://web.archive.org/web/20240321170036/https://paper.bobylive.com/Security/CIS/CIS_Apple_macOS_10_14_Benchmark_v1_4_0_PDF.pdf "CIS Apple macOS 10.14 Benchmark v1.4.0 | paper.bobylive.com"
[5]: https://web.archive.org/web/20240321190244/https://github-wiki-see.page/m/edamametechnologies/threatmodels/wiki/threatmodel-macOS-EN "threatmodel macOS EN - edamametechnologies/threatmodels GitHub Wiki | github-wiki-see.page"
[6]: https://web.archive.org/web/20240321190315/https://www.tenable.com/audits/items/CIS_Apple_macOS_14.0_Sonoma_v1.0.0_L1.audit:66d3b86318384ba7947a3409e0c6e902 "1.5 Ensure Install Application Updates from the App Store Is E... | Tenable® | www.tenable.com"
[7]: https://web.archive.org/web/20240321165304/https://derflounder.wordpress.com/2014/12/29/managing-automatic-app-store-and-os-x-update-installation-on-yosemite/ "Managing automatic App Store and OS X update installation on Yosemite | Der Flounder | derflounder.wordpress.com"
[8]: https://web.archive.org/web/20240321190410/https://krypted.com/mac-security/app-store-preferences-set-server-5-2-macos-sierra/ "App Store Preferences To Set In On Server 5.2 for macOS Sierra krypted | krypted.com"
[4]: https://web.archive.org/web/20240321165931/https://csrc.nist.gov/CSRC/media/Projects/national-vulnerability-database/documents/CCE/CCE-macos_monterey.xls "CCE-91129-7 | CCE-macos_monterey.xls | Sheet 1 - NIST Computer Security Resource Center | csrc.nist.gov"
[9]: https://web.archive.org/web/20240321190114/https://www.irs.gov/pub/irs-utl/safeguards-scsem-macosx.xlsx "SCSEM OSX 13.0 | Internal Revenue Service Office of Safeguards | www.irs.gov"
[10]: https://archive.ph/2024.03.21-180353/https://developer.apple.com/documentation/devicemanagement/softwareupdate "SoftwareUpdate | Apple Developer Documentation | developer.apple.com"
[11]: https://web.archive.org/web/20240321190122/https://paper.bobylive.com/Security/CIS/CIS_Apple_macOS_12_0_Monterey_Benchmark_v1_0_0.pdf "CIS Apple macOS 12.0 Monterey | CIS Benchmarks | paper.bobylive.com"
[12]: https://web.archive.org/web/20240321190537/https://www.tenable.com/audits/items/CIS_Apple_macOS_11_v2.0.0_L1.audit:55e8759872dce781b8dbc5a3f42e23b9 "1.4 Ensure Installation of App Update Is Enabled | Tenable® | www.tenable.com"
[13]: https://web.archive.org/web/20240321164917/https://www.ncsc.gov.uk/files/macos_provisioning_script.sh_.txt "macOS provisioning script | UK National Cyber Security Centre | www.ncsc.gov.uk"
code: |- code: |-
# For OS X Yosemite and newer (>= 10.10) # For OS X Yosemite and newer (>= 10.10)
sudo defaults write /Library/Preferences/com.apple.commerce 'AutoUpdate' -bool false sudo defaults write /Library/Preferences/com.apple.commerce 'AutoUpdate' -bool false
@@ -1507,7 +1587,7 @@ actions:
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticallyInstallAppUpdates' -bool true sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AutomaticallyInstallAppUpdates' -bool true
- -
name: Disable macOS beta release installation name: Disable macOS beta release installation
docs: https://support.apple.com/en-gb/HT203018 docs: https://web.archive.org/web/20170106103856/https://support.apple.com/en-gb/HT203018
code: |- code: |-
# For OS X Yosemite and newer (>= 10.10) # For OS X Yosemite and newer (>= 10.10)
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AllowPreReleaseInstallation' -bool false sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AllowPreReleaseInstallation' -bool false
@@ -1516,7 +1596,7 @@ actions:
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AllowPreReleaseInstallation' -bool true sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'AllowPreReleaseInstallation' -bool true
- -
name: Disable automatic installation for configuration data (e.g. XProtect, Gatekeeper, MRT) name: Disable automatic installation for configuration data (e.g. XProtect, Gatekeeper, MRT)
docs: https://derflounder.wordpress.com/2018/12/28/enabling-automatic-macos-software-updates-for-os-x-yosemite-through-macos-mojave/ docs: https://web.archive.org/web/20240321170251/https://derflounder.wordpress.com/2018/12/28/enabling-automatic-macos-software-updates-for-os-x-yosemite-through-macos-mojave/
code: |- code: |-
# For OS X Yosemite and newer (>= 10.10) # For OS X Yosemite and newer (>= 10.10)
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'ConfigDataInstall' -bool false sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'ConfigDataInstall' -bool false
@@ -1525,12 +1605,47 @@ actions:
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'ConfigDataInstall' -bool true sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'ConfigDataInstall' -bool true
- -
name: Disable automatic installation for system data files and security updates name: Disable automatic installation for system data files and security updates
docs: docs: |-
# References for CriticalUpdateInstall This script stops automatic installations of critical updates [1],
- https://derflounder.wordpress.com/2014/12/24/managing-os-xs-automatic-security-updates/ including security [1] [2] [3] [4] [5] [6] [7] and system data file [1] [8] updates.
- https://developer.apple.com/documentation/devicemanagement/softwareupdate
# References for softwareupdate --background-critical It improves privacy by providing:
- https://managingosx.wordpress.com/2013/04/30/undocumented-options/
- **Control Over Update Timing**:
Users can review updates before installation to ensure they meet privacy standards and do not introduce
unwanted telemetry or changes.
- **Reduced External Communications**:
Reduces how often it connects to update servers, potentially protection user information.
The script configures the `/Library/Preferences/com.apple.SoftwareUpdate!CriticalUpdateInstall` setting [1] [4] [5] [7] [8].
This action prevents automatic downloads and installations of updates [1].
It also prevents users from changing the Install system data files and security updates option manually [1].
This script is compatible with OS X Yosemite and later versions [6] [8].
The revert script triggers `softwareupdate --background-critical` to install any pending critical updates directly [2] [9].
> **Caution:**
> Only disable automatic updates if you're committed to manually installing them quickly to maintain your computer's security [4] [5] [8].
> It's important to install updates soon to protect your computer. [4] [5] [8].
>
> This script disables:
>
> - Definition updates for **XProtect** and **Gatekeeper** that keep your computer safe from new threats [5].
> - **Rapid Security Response** [10] [11].
> **Rapid Security Responses** are software releases providing important security improvements between standard updates [12].
[1]: https://archive.ph/2024.03.21-180353/https://developer.apple.com/documentation/devicemanagement/softwareupdate "SoftwareUpdate | Apple Developer Documentation | developer.apple.com"
[2]: https://web.archive.org/web/20240321201417/https://derflounder.wordpress.com/2014/12/24/managing-os-xs-automatic-security-updates/ "Managing OS Xs automatic security updates | Der Flounder | derflounder.wordpress.com"
[3]: https://web.archive.org/web/20240321165118/https://macadminsdoc.readthedocs.io/en/master/Profiles-and-Settings/OS-X-Updates.html "macOS Updates — MacAdmins Community Documentation documentation | macadminsdoc.readthedocs.io"
[4]: https://web.archive.org/web/20240321165931/https://csrc.nist.gov/CSRC/media/Projects/national-vulnerability-database/documents/CCE/CCE-macos_monterey.xls "CCE-91129-7 | CCE-macos_monterey.xls | Sheet 1 - NIST Computer Security Resource Center | csrc.nist.gov"
[5]: https://web.archive.org/web/20240321201450/https://paper.bobylive.com/Security/CIS/CIS_Apple_OSX_10_9_Benchmark_v1_3_0.pdf "CIS Apple OSX 10.9 Benchmark | paper.bobylive.com"
[6]: https://web.archive.org/web/20240321201643/https://derflounder.wordpress.com/2014/12/27/managing-automatic-installation-of-configdata-and-security-software-updates-on-yosemite/ "Managing automatic installation of ConfigData and security software updates on Yosemite | Der Flounder | derflounder.wordpress.com"
[7]: https://web.archive.org/web/20240321201652/https://ss64.com/mac/syntax-defaults.html "System preference settings for macOS - macOS - SS64.com | ss64.com"
[8]: https://web.archive.org/web/20240321201436/https://www.tenable.com/audits/items/CIS_OSX_10.10_v1.2.0_L1.audit:97f36c2eaa06045e85a1beff1a76a088 "1.4 Enable system data files and security update installs - 'C... | Tenable® | www.tenable.com"
[9]: https://web.archive.org/web/20240321201406/https://managingosx.wordpress.com/2013/04/30/undocumented-options/ "Undocumented options Managing OS X | managingosx.wordpress.com"
[10]: https://web.archive.org/web/20240321201558/https://www.intuneirl.com/rapid-security-response/ "Managing Rapid Security Response on Apple Devices | www.intuneirl.com"
[11]: https://web.archive.org/web/20240321201614/https://onsitegroup.co.za/rapid-security-response/ "Rapid security response - Onsite | onsitegroup.co.za"
[12]: https://web.archive.org/web/20240321201623/https://support.apple.com/en-us/102657 "About Rapid Security Responses for iOS, iPadOS, and macOS - Apple Support | support.apple.com"
code: |- code: |-
# For OS X Yosemite and newer (>= 10.10) # For OS X Yosemite and newer (>= 10.10)
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'CriticalUpdateInstall' -bool false sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate 'CriticalUpdateInstall' -bool false

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,10 @@
export type SchedulerCallbackType = (...args: unknown[]) => void; export type SchedulerCallbackType = (...args: unknown[]) => void;
export type SchedulerType = (callback: SchedulerCallbackType, ms: number) => void; export type SchedulerType = (callback: SchedulerCallbackType, ms: number) => void;
export function sleep(time: number, scheduler: SchedulerType = setTimeout) { export function sleep(
time: number,
scheduler: SchedulerType = setTimeout,
): Promise<void> {
return new Promise((resolve) => { return new Promise((resolve) => {
scheduler(() => resolve(undefined), time); scheduler(() => resolve(undefined), time);
}); });

View File

@@ -128,3 +128,8 @@
$calculated-width-in-em: calc(#{$estimated-width-per-character-in-em} * #{$value-in-ch}); $calculated-width-in-em: calc(#{$estimated-width-per-character-in-em} * #{$value-in-ch});
#{$property}: $calculated-width-in-em; #{$property}: $calculated-width-in-em;
} }
@mixin base-font-style {
font-family: $font-family-main;
font-size: $font-size-absolute-normal;
}

View File

@@ -21,9 +21,7 @@ $base-spacing: 1em;
body { body {
background: $color-background; background: $color-background;
font-family: $font-family-main; @include base-font-style;
font-size: $font-size-absolute-normal;
@include apply-uniform-spacing($base-spacing: $base-spacing) @include apply-uniform-spacing($base-spacing: $base-spacing)
} }

View File

@@ -220,7 +220,9 @@ $color-tooltip-background: $color-primary-darkest;
color: $color-on-primary; color: $color-on-primary;
border-radius: 16px; border-radius: 16px;
padding: 12px 10px; padding: 12px 10px;
font-size: $font-size-absolute-normal;
// Explicitly set font styling for tooltips to prevent inconsistent appearances due to style inheritance from trigger elements.
@include base-font-style;
/* /*
This margin creates a visual buffer between the tooltip and the edges of the document. This margin creates a visual buffer between the tooltip and the edges of the document.

View File

@@ -13,4 +13,4 @@ export const RENDERER_URL = process.env.ELECTRON_RENDERER_URL;
export const RENDERER_HTML_PATH = join('file://', __dirname, '../renderer/index.html'); export const RENDERER_HTML_PATH = join('file://', __dirname, '../renderer/index.html');
export const PRELOADER_SCRIPT_PATH = join(__dirname, '../preload/index.cjs'); export const PRELOADER_SCRIPT_PATH = join(__dirname, '../preload/index.mjs');

View File

@@ -1,18 +1,20 @@
import { app, dialog } from 'electron/main'; import { app, dialog } from 'electron/main';
import { autoUpdater, type UpdateInfo } from 'electron-updater';
import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger'; import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger';
import { UpdateProgressBar } from './UpdateProgressBar'; import { UpdateProgressBar } from './UpdateProgressBar';
import { getAutoUpdater } from './ElectronAutoUpdaterFactory';
import type { AppUpdater, UpdateInfo } from 'electron-updater';
import type { ProgressInfo } from 'electron-builder'; import type { ProgressInfo } from 'electron-builder';
export async function handleAutoUpdate() { export async function handleAutoUpdate() {
const autoUpdater = getAutoUpdater();
if (await askDownloadAndInstall() === DownloadDialogResult.NotNow) { if (await askDownloadAndInstall() === DownloadDialogResult.NotNow) {
return; return;
} }
startHandlingUpdateProgress(); startHandlingUpdateProgress(autoUpdater);
await autoUpdater.downloadUpdate(); await autoUpdater.downloadUpdate();
} }
function startHandlingUpdateProgress() { function startHandlingUpdateProgress(autoUpdater: AppUpdater) {
const progressBar = new UpdateProgressBar(); const progressBar = new UpdateProgressBar();
progressBar.showIndeterminateState(); progressBar.showIndeterminateState();
autoUpdater.on('error', (e) => { autoUpdater.on('error', (e) => {
@@ -29,11 +31,11 @@ function startHandlingUpdateProgress() {
autoUpdater.on('update-downloaded', async (info: UpdateInfo) => { autoUpdater.on('update-downloaded', async (info: UpdateInfo) => {
ElectronLogger.info('@update-downloaded@\n', info); ElectronLogger.info('@update-downloaded@\n', info);
progressBar.close(); progressBar.close();
await handleUpdateDownloaded(); await handleUpdateDownloaded(autoUpdater);
}); });
} }
async function handleUpdateDownloaded() { async function handleUpdateDownloaded(autoUpdater: AppUpdater) {
if (await askRestartAndInstall() === InstallDialogResult.NotNow) { if (await askRestartAndInstall() === InstallDialogResult.NotNow) {
return; return;
} }

View File

@@ -0,0 +1,8 @@
import electronUpdater, { type AppUpdater } from 'electron-updater';
export function getAutoUpdater(): AppUpdater {
// Using destructuring to access autoUpdater due to the CommonJS module of 'electron-updater'.
// It is a workaround for ESM compatibility issues, see https://github.com/electron-userland/electron-builder/issues/7976.
const { autoUpdater } = electronUpdater;
return autoUpdater;
}

View File

@@ -1,13 +1,16 @@
import { autoUpdater, type UpdateInfo } from 'electron-updater';
import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger'; import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger';
import { requiresManualUpdate, startManualUpdateProcess } from './ManualUpdater/ManualUpdateCoordinator'; import { requiresManualUpdate, startManualUpdateProcess } from './ManualUpdater/ManualUpdateCoordinator';
import { handleAutoUpdate } from './AutomaticUpdateCoordinator'; import { handleAutoUpdate } from './AutomaticUpdateCoordinator';
import { getAutoUpdater } from './ElectronAutoUpdaterFactory';
import type { UpdateInfo } from 'electron-updater';
interface Updater { interface Updater {
checkForUpdates(): Promise<void>; checkForUpdates(): Promise<void>;
} }
export function setupAutoUpdater(): Updater { export function setupAutoUpdater(): Updater {
const autoUpdater = getAutoUpdater();
autoUpdater.logger = ElectronLogger; autoUpdater.logger = ElectronLogger;
// Auto-downloads are disabled to allow separate handling of 'check' and 'download' actions, // Auto-downloads are disabled to allow separate handling of 'check' and 'download' actions,

View File

@@ -1,4 +1,4 @@
import { splitTextIntoLines, indentText } from '../utils/text'; import { indentText, splitTextIntoLines } from '@tests/shared/Text';
import { log, die } from '../utils/log'; import { log, die } from '../utils/log';
import { readAppLogFile } from './app-logs'; import { readAppLogFile } from './app-logs';
import { STDERR_IGNORE_PATTERNS } from './error-ignore-patterns'; import { STDERR_IGNORE_PATTERNS } from './error-ignore-patterns';

View File

@@ -1,7 +1,7 @@
import { filterEmpty } from '@tests/shared/Text';
import { runCommand } from '../../utils/run-command'; import { runCommand } from '../../utils/run-command';
import { log, LogLevel } from '../../utils/log'; import { log, LogLevel } from '../../utils/log';
import { SupportedPlatform, CURRENT_PLATFORM } from '../../utils/platform'; import { SupportedPlatform, CURRENT_PLATFORM } from '../../utils/platform';
import { filterEmpty } from '../../utils/text';
export async function captureWindowTitles(processId: number) { export async function captureWindowTitles(processId: number) {
if (!processId) { throw new Error('Missing process ID.'); } if (!processId) { throw new Error('Missing process ID.'); }

View File

@@ -1,3 +1,4 @@
import { indentText } from '@tests/shared/Text';
import { logCurrentArgs, CommandLineFlag, hasCommandLineFlag } from './cli-args'; import { logCurrentArgs, CommandLineFlag, hasCommandLineFlag } from './cli-args';
import { log, die } from './utils/log'; import { log, die } from './utils/log';
import { ensureNpmProjectDir, npmInstall, npmBuild } from './utils/npm'; import { ensureNpmProjectDir, npmInstall, npmBuild } from './utils/npm';
@@ -15,7 +16,6 @@ import {
APP_EXECUTION_DURATION_IN_SECONDS, APP_EXECUTION_DURATION_IN_SECONDS,
SCREENSHOT_PATH, SCREENSHOT_PATH,
} from './config'; } from './config';
import { indentText } from './utils/text';
import type { ExtractionResult } from './app/extractors/common/extraction-result'; import type { ExtractionResult } from './app/extractors/common/extraction-result';
export async function main(): Promise<void> { export async function main(): Promise<void> {

View File

@@ -1,5 +1,6 @@
import { exec, type ExecOptions, type ExecException } from 'node:child_process'; import { exec } from 'child_process';
import { indentText } from './text'; import { indentText } from '@tests/shared/Text';
import type { ExecOptions, ExecException } from 'child_process';
const TIMEOUT_IN_SECONDS = 180; const TIMEOUT_IN_SECONDS = 180;
const MAX_OUTPUT_BUFFER_SIZE = 1024 * 1024; // 1 MB const MAX_OUTPUT_BUFFER_SIZE = 1024 * 1024; // 1 MB

View File

@@ -1,6 +1,7 @@
import { import {
describe, it, beforeAll, afterAll, describe, it, beforeAll, afterAll,
} from 'vitest'; } from 'vitest';
import { formatAssertionMessage } from '@tests/shared/FormatAssertionMessage';
import { main } from './check-desktop-runtime-errors/main'; import { main } from './check-desktop-runtime-errors/main';
import { COMMAND_LINE_FLAGS, CommandLineFlag } from './check-desktop-runtime-errors/cli-args'; import { COMMAND_LINE_FLAGS, CommandLineFlag } from './check-desktop-runtime-errors/cli-args';
@@ -14,7 +15,10 @@ describe('desktop runtime error checks', () => {
() => main(), () => main(),
); );
// assert // assert
expect(exitCode).to.equal(0); expect(exitCode).to.equal(0, formatAssertionMessage([
`Test failed with exit code ${exitCode}; expected 0.`,
'Examine preceding logs to identify errors.',
]));
}, { }, {
timeout: 60 /* minutes */ * 60000, timeout: 60 /* minutes */ * 60000,
}); });

View File

@@ -1,64 +1,65 @@
import { sleep } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
import { getUrlStatus, type IRequestOptions } from './Requestor'; import { getUrlStatus, type RequestOptions } from './Requestor';
import { groupUrlsByDomain } from './UrlPerDomainGrouper'; import { groupUrlsByDomain } from './UrlDomainProcessing';
import type { IUrlStatus } from './IUrlStatus'; import type { FollowOptions } from './FetchFollow';
import type { UrlStatus } from './UrlStatus';
export async function getUrlStatusesInParallel( export async function getUrlStatusesInParallel(
urls: string[], urls: string[],
options?: IBatchRequestOptions, options?: BatchRequestOptions,
): Promise<IUrlStatus[]> { ): Promise<UrlStatus[]> {
// urls = [ 'https://privacy.sexy' ]; // Here to comment out when testing // urls = ['https://privacy.sexy']; // Comment out this line to use a hardcoded URL for testing.
const uniqueUrls = Array.from(new Set(urls)); const uniqueUrls = Array.from(new Set(urls));
const defaultedOptions = { ...DefaultOptions, ...options }; const defaultedDomainOptions: Required<DomainOptions> = {
console.log('Options: ', defaultedOptions); ...DefaultDomainOptions,
const results = await request(uniqueUrls, defaultedOptions); ...options?.domainOptions,
};
console.log('Batch request options applied:', defaultedDomainOptions);
const results = await request(uniqueUrls, defaultedDomainOptions, options);
return results; return results;
} }
export interface IBatchRequestOptions { export interface BatchRequestOptions {
domainOptions?: IDomainOptions; readonly domainOptions?: Partial<DomainOptions>;
requestOptions?: IRequestOptions; readonly requestOptions?: Partial<RequestOptions>;
readonly followOptions?: Partial<FollowOptions>;
} }
interface IDomainOptions { interface DomainOptions {
sameDomainParallelize?: boolean; readonly sameDomainParallelize?: boolean;
sameDomainDelayInMs?: number; readonly sameDomainDelayInMs?: number;
} }
const DefaultOptions: Required<IBatchRequestOptions> = { const DefaultDomainOptions: Required<DomainOptions> = {
domainOptions: { sameDomainParallelize: false,
sameDomainParallelize: false, sameDomainDelayInMs: 3 /* sec */ * 1000,
sameDomainDelayInMs: 3 /* sec */ * 1000,
},
requestOptions: {
retryExponentialBaseInMs: 5 /* sec */ * 1000,
requestTimeoutInMs: 60 /* sec */ * 1000,
additionalHeaders: {},
},
}; };
function request( function request(
urls: string[], urls: string[],
options: Required<IBatchRequestOptions>, domainOptions: Required<DomainOptions>,
): Promise<IUrlStatus[]> { options?: BatchRequestOptions,
if (!options.domainOptions.sameDomainParallelize) { ): Promise<UrlStatus[]> {
if (!domainOptions.sameDomainParallelize) {
return runOnEachDomainWithDelay( return runOnEachDomainWithDelay(
urls, urls,
(url) => getUrlStatus(url, options.requestOptions), (url) => getUrlStatus(url, options?.requestOptions, options?.followOptions),
options.domainOptions.sameDomainDelayInMs, domainOptions.sameDomainDelayInMs,
); );
} }
return Promise.all(urls.map((url) => getUrlStatus(url, options.requestOptions))); return Promise.all(
urls.map((url) => getUrlStatus(url, options?.requestOptions, options?.followOptions)),
);
} }
async function runOnEachDomainWithDelay( async function runOnEachDomainWithDelay(
urls: string[], urls: string[],
action: (url: string) => Promise<IUrlStatus>, action: (url: string) => Promise<UrlStatus>,
delayInMs: number | undefined, delayInMs: number | undefined,
): Promise<IUrlStatus[]> { ): Promise<UrlStatus[]> {
const grouped = groupUrlsByDomain(urls); const grouped = groupUrlsByDomain(urls);
const tasks = grouped.map(async (group) => { const tasks = grouped.map(async (group) => {
const results = new Array<IUrlStatus>(); const results = new Array<UrlStatus>();
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
for (const url of group) { for (const url of group) {
const status = await action(url); const status = await action(url);

View File

@@ -1,27 +1,33 @@
import { sleep } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
import type { IUrlStatus } from './IUrlStatus'; import { indentText } from '@tests/shared/Text';
import { type UrlStatus, formatUrlStatus } from './UrlStatus';
const DefaultBaseRetryIntervalInMs = 5 /* sec */ * 1000; const DefaultBaseRetryIntervalInMs = 5 /* sec */ * 1000;
export async function retryWithExponentialBackOff( export async function retryWithExponentialBackOff(
action: () => Promise<IUrlStatus>, action: () => Promise<UrlStatus>,
baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs, baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs,
currentRetry = 1, currentRetry = 1,
): Promise<IUrlStatus> { ): Promise<UrlStatus> {
const maxTries = 3; const maxTries = 3;
const status = await action(); const status = await action();
if (shouldRetry(status)) { if (shouldRetry(status)) {
if (currentRetry <= maxTries) { if (currentRetry <= maxTries) {
const exponentialBackOffInMs = getRetryTimeoutInMs(currentRetry, baseRetryIntervalInMs); const exponentialBackOffInMs = getRetryTimeoutInMs(currentRetry, baseRetryIntervalInMs);
console.log(`Retrying (${currentRetry}) in ${exponentialBackOffInMs / 1000} seconds`, status); console.log([
`Attempt ${currentRetry}: Retrying in ${exponentialBackOffInMs / 1000} seconds.`,
'Details:',
indentText(formatUrlStatus(status)),
].join('\n'));
await sleep(exponentialBackOffInMs); await sleep(exponentialBackOffInMs);
return retryWithExponentialBackOff(action, baseRetryIntervalInMs, currentRetry + 1); return retryWithExponentialBackOff(action, baseRetryIntervalInMs, currentRetry + 1);
} }
console.warn('💀 All retry attempts failed. Final failure to retrieve URL:', indentText(formatUrlStatus(status)));
} }
return status; return status;
} }
function shouldRetry(status: IUrlStatus) { function shouldRetry(status: UrlStatus): boolean {
if (status.error) { if (status.error) {
return true; return true;
} }
@@ -32,14 +38,14 @@ function shouldRetry(status: IUrlStatus) {
|| status.code === 429; // Too Many Requests || status.code === 429; // Too Many Requests
} }
function isTransientError(statusCode: number) { function isTransientError(statusCode: number): boolean {
return statusCode >= 500 && statusCode <= 599; return statusCode >= 500 && statusCode <= 599;
} }
function getRetryTimeoutInMs( function getRetryTimeoutInMs(
currentRetry: number, currentRetry: number,
baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs, baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs,
) { ): number {
const retryRandomFactor = 0.5; // Retry intervals are between 50% and 150% const retryRandomFactor = 0.5; // Retry intervals are between 50% and 150%
// of the exponentially increasing base amount // of the exponentially increasing base amount
const minRandom = 1 - retryRandomFactor; const minRandom = 1 - retryRandomFactor;

View File

@@ -1,19 +1,22 @@
import { indentText } from '@tests/shared/Text';
import { fetchWithTimeout } from './FetchWithTimeout'; import { fetchWithTimeout } from './FetchWithTimeout';
import { getDomainFromUrl } from './UrlDomainProcessing';
export function fetchFollow( export function fetchFollow(
url: string, url: string,
timeoutInMs: number, timeoutInMs: number,
fetchOptions: RequestInit, fetchOptions?: Partial<RequestInit>,
followOptions: IFollowOptions | undefined, followOptions?: Partial<FollowOptions>,
): Promise<Response> { ): Promise<Response> {
const defaultedFollowOptions = { const defaultedFollowOptions: Required<FollowOptions> = {
...DefaultFollowOptions, ...DefaultFollowOptions,
...followOptions, ...followOptions,
}; };
if (followRedirects(defaultedFollowOptions)) { console.log(indentText(`Follow options: ${JSON.stringify(defaultedFollowOptions)}`));
if (!followRedirects(defaultedFollowOptions)) {
return fetchWithTimeout(url, timeoutInMs, fetchOptions); return fetchWithTimeout(url, timeoutInMs, fetchOptions);
} }
fetchOptions = { ...fetchOptions, redirect: 'manual' /* handled manually */ }; fetchOptions = { ...fetchOptions, redirect: 'manual' /* handled manually */, mode: 'cors' };
const cookies = new CookieStorage(defaultedFollowOptions.enableCookies); const cookies = new CookieStorage(defaultedFollowOptions.enableCookies);
return followRecursivelyWithCookies( return followRecursivelyWithCookies(
url, url,
@@ -24,13 +27,13 @@ export function fetchFollow(
); );
} }
export interface IFollowOptions { export interface FollowOptions {
followRedirects?: boolean; readonly followRedirects?: boolean;
maximumRedirectFollowDepth?: number; readonly maximumRedirectFollowDepth?: number;
enableCookies?: boolean; readonly enableCookies?: boolean;
} }
export const DefaultFollowOptions: Required<IFollowOptions> = { const DefaultFollowOptions: Required<FollowOptions> = {
followRedirects: true, followRedirects: true,
maximumRedirectFollowDepth: 20, maximumRedirectFollowDepth: 20,
enableCookies: true, enableCookies: true,
@@ -64,6 +67,10 @@ async function followRecursivelyWithCookies(
if (cookieHeader) { if (cookieHeader) {
cookies.addHeader(cookieHeader); cookies.addHeader(cookieHeader);
} }
options.headers = {
...options.headers,
Host: getDomainFromUrl(nextUrl),
};
return followRecursivelyWithCookies(nextUrl, timeoutInMs, options, newFollowDepth, cookies); return followRecursivelyWithCookies(nextUrl, timeoutInMs, options, newFollowDepth, cookies);
} }
@@ -77,7 +84,7 @@ class CookieStorage {
constructor(private readonly enabled: boolean) { constructor(private readonly enabled: boolean) {
} }
public hasAny() { public hasAny(): boolean {
return this.enabled && this.cookies.length > 0; return this.enabled && this.cookies.length > 0;
} }
@@ -88,17 +95,17 @@ class CookieStorage {
this.cookies.push(header); this.cookies.push(header);
} }
public getHeader() { public getHeader(): string {
return this.cookies.join(' ; '); return this.cookies.join(' ; ');
} }
} }
function followRedirects(options: IFollowOptions) { function followRedirects(options: FollowOptions): boolean {
if (!options.followRedirects) { if (options.followRedirects !== true) {
return false; return false;
} }
if (options.maximumRedirectFollowDepth === 0) { if (options.maximumRedirectFollowDepth === undefined || options.maximumRedirectFollowDepth <= 0) {
return false; throw new Error('Invalid followRedirects configuration: maximumRedirectFollowDepth must be a positive integer');
} }
return true; return true;
} }

View File

@@ -2,13 +2,13 @@ export async function fetchWithTimeout(
url: string, url: string,
timeoutInMs: number, timeoutInMs: number,
init?: RequestInit, init?: RequestInit,
): Promise<Response> { ): ReturnType<typeof fetch> {
const controller = new AbortController();
const options: RequestInit = { const options: RequestInit = {
...(init ?? {}), ...(init ?? {}),
signal: controller.signal, signal: AbortSignal.timeout(timeoutInMs),
}; };
const promise = fetch(url, options); return fetch(
const timeout = setTimeout(() => controller.abort(), timeoutInMs); url,
return promise.finally(() => clearTimeout(timeout)); options,
);
} }

View File

@@ -1,5 +0,0 @@
export interface IUrlStatus {
url: string;
error?: string;
code?: number;
}

View File

@@ -13,7 +13,10 @@ A CLI and SDK for checking the availability of external URLs.
- 😇 **Rate Limiting**: Queues requests by domain to be polite. - 😇 **Rate Limiting**: Queues requests by domain to be polite.
- 🔁 **Retries**: Implements retry pattern with exponential back-off. - 🔁 **Retries**: Implements retry pattern with exponential back-off.
-**Timeouts**: Configurable timeout for each request. -**Timeouts**: Configurable timeout for each request.
- 🎭️ **User-Agent Rotation**: Change user agents for each request. - 🎭️ **Impersonation**: Impersonate different browsers for each request.
- **🌐 User-Agent Rotation**: Change user agents.
- **🔑 TLS Handshakes**: Perform TLS and HTTP handshakes that are identical to that of a real browser.
- 🫙 **Cookie jar**: Preserve cookies during redirects to mimic real browser.
## CLI ## CLI
@@ -54,6 +57,7 @@ const statuses = await getUrlStatusesInParallel([ 'https://privacy.sexy', /* ...
- **`sameDomainDelayInMs`** (*number*), default: `3000` (3 seconds) - **`sameDomainDelayInMs`** (*number*), default: `3000` (3 seconds)
- Sets the delay between requests to the same domain. - Sets the delay between requests to the same domain.
- `requestOptions` (*object*): See [request options](#request-options). - `requestOptions` (*object*): See [request options](#request-options).
- `followOptions` (*object*): See [follow options](#follow-options).
### `getUrlStatus` ### `getUrlStatus`
@@ -72,7 +76,6 @@ console.log(`Status code: ${status.code}`);
- The longer the base time, the greater the intervals between retries. - The longer the base time, the greater the intervals between retries.
- **`additionalHeaders`** (*object*), default: `false` - **`additionalHeaders`** (*object*), default: `false`
- Additional HTTP headers to send along with the default headers. Overrides default headers if specified. - Additional HTTP headers to send along with the default headers. Overrides default headers if specified.
- **`followOptions`** (*object*): See [follow options](#follow-options).
- **`requestTimeoutInMs`** (*number*), default: `60000` (60 seconds) - **`requestTimeoutInMs`** (*number*), default: `60000` (60 seconds)
- Time limit to abort the request if no response is received within the specified time frame. - Time limit to abort the request if no response is received within the specified time frame.
@@ -83,19 +86,7 @@ Follows `3XX` redirects while preserving cookies.
Same fetch API except third parameter that specifies [follow options](#follow-options), `redirect: 'follow' | 'manual' | 'error'` is discarded in favor of the third parameter. Same fetch API except third parameter that specifies [follow options](#follow-options), `redirect: 'follow' | 'manual' | 'error'` is discarded in favor of the third parameter.
```js ```js
const status = await fetchFollow('https://privacy.sexy', { const status = await fetchFollow('https://privacy.sexy', 1000 /* timeout in milliseconds */);
// First argument is same options as fetch API, except `redirect` options
// that's discarded in favor of next argument follow options
headers: {
'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0'
},
}, {
// Second argument sets the redirect behavior
followRedirects: true,
maximumRedirectFollowDepth: 20,
enableCookies: true,
}
);
console.log(`Status code: ${status.code}`); console.log(`Status code: ${status.code}`);
``` ```
@@ -109,3 +100,10 @@ console.log(`Status code: ${status.code}`);
- **`enableCookies`** (*boolean*), default: `true` - **`enableCookies`** (*boolean*), default: `true`
- Enables cookie storage to facilitate seamless navigation through login or other authentication challenges. - Enables cookie storage to facilitate seamless navigation through login or other authentication challenges.
- 💡 Helps to over-come sign-in challenges with callbacks. - 💡 Helps to over-come sign-in challenges with callbacks.
- **`forceHttpGetForUrlPatterns`** (*array*), default: `[]`
- Specifies URL patterns that should always use an HTTP GET request instead of the default HTTP HEAD.
- This is useful for websites that do not respond to HEAD requests, such as those behind certain CDN or web application firewalls.
- Provide patterns as regular expressions (`RegExp`), allowing them to match any part of a URL.
- Examples:
- To match any URL starting with "https://example.com/api": `/^https:\/\/example\.com\/api/`
- To match any domain ending with "cloudflare.com": `/^https:\/\/.*\.cloudflare\.com\//`

View File

@@ -1,70 +1,123 @@
import { indentText } from '@tests/shared/Text';
import { retryWithExponentialBackOff } from './ExponentialBackOffRetryHandler'; import { retryWithExponentialBackOff } from './ExponentialBackOffRetryHandler';
import { fetchFollow, type IFollowOptions, DefaultFollowOptions } from './FetchFollow'; import { fetchFollow, type FollowOptions } from './FetchFollow';
import { getRandomUserAgent } from './UserAgents'; import { getRandomUserAgent } from './UserAgents';
import type { IUrlStatus } from './IUrlStatus'; import { getDomainFromUrl } from './UrlDomainProcessing';
import { randomizeTlsFingerprint, getTlsContextInfo } from './TlsFingerprintRandomizer';
import type { UrlStatus } from './UrlStatus';
export function getUrlStatus( export function getUrlStatus(
url: string, url: string,
options: IRequestOptions = DefaultOptions, requestOptions?: Partial<RequestOptions>,
): Promise<IUrlStatus> { followOptions?: Partial<FollowOptions>,
const defaultedOptions = { ...DefaultOptions, ...options }; ): Promise<UrlStatus> {
const fetchOptions = getFetchOptions(url, defaultedOptions); const defaultedOptions = getDefaultedRequestOptions(requestOptions);
return retryWithExponentialBackOff(async () => { if (defaultedOptions.randomizeTlsFingerprint) {
console.log('Requesting', url); randomizeTlsFingerprint();
let result: IUrlStatus; }
try { return fetchUrlStatusWithRetry(url, defaultedOptions, followOptions);
const response = await fetchFollow(
url,
defaultedOptions.requestTimeoutInMs,
fetchOptions,
defaultedOptions.followOptions,
);
result = { url, code: response.status };
} catch (err) {
result = { url, error: JSON.stringify(err, null, '\t') };
}
return result;
}, defaultedOptions.retryExponentialBaseInMs);
} }
export interface IRequestOptions { export interface RequestOptions {
readonly retryExponentialBaseInMs?: number; readonly retryExponentialBaseInMs?: number;
readonly additionalHeaders?: Record<string, string>; readonly additionalHeaders?: Record<string, string>;
readonly additionalHeadersUrlIgnore?: string[]; readonly additionalHeadersUrlIgnore?: string[];
readonly followOptions?: IFollowOptions;
readonly requestTimeoutInMs: number; readonly requestTimeoutInMs: number;
readonly randomizeTlsFingerprint: boolean;
readonly forceHttpGetForUrlPatterns: RegExp[];
} }
const DefaultOptions: Required<IRequestOptions> = { const DefaultOptions: Required<RequestOptions> = {
retryExponentialBaseInMs: 5000, retryExponentialBaseInMs: 5 /* sec */ * 1000,
additionalHeaders: {}, additionalHeaders: {},
additionalHeadersUrlIgnore: [], additionalHeadersUrlIgnore: [],
requestTimeoutInMs: 60 /* seconds */ * 1000, requestTimeoutInMs: 60 /* seconds */ * 1000,
followOptions: DefaultFollowOptions, randomizeTlsFingerprint: true,
forceHttpGetForUrlPatterns: [],
}; };
function getFetchOptions(url: string, options: Required<IRequestOptions>): RequestInit { function fetchUrlStatusWithRetry(
url: string,
requestOptions: Required<RequestOptions>,
followOptions?: Partial<FollowOptions>,
): Promise<UrlStatus> {
const fetchOptions = getFetchOptions(url, requestOptions);
return retryWithExponentialBackOff(async () => {
console.log(`🚀 Initiating request for URL: ${url}`);
console.log(indentText([
`HTTP method: ${fetchOptions.method}`,
`Request options: ${JSON.stringify(requestOptions)}`,
].join('\n')));
let result: UrlStatus;
try {
const response = await fetchFollow(
url,
requestOptions.requestTimeoutInMs,
fetchOptions,
followOptions,
);
result = { url, code: response.status };
} catch (err) {
result = {
url,
error: [
'Error:', indentText(JSON.stringify(err, null, '\t') || err.toString()),
'Fetch options:', indentText(JSON.stringify(fetchOptions, null, '\t')),
'Request options:', indentText(JSON.stringify(requestOptions, null, '\t')),
'TLS:', indentText(getTlsContextInfo()),
].join('\n'),
};
}
return result;
}, requestOptions.retryExponentialBaseInMs);
}
function getFetchOptions(url: string, options: Required<RequestOptions>): RequestInit {
const additionalHeaders = options.additionalHeadersUrlIgnore const additionalHeaders = options.additionalHeadersUrlIgnore
.some((ignorePattern) => url.startsWith(ignorePattern)) .some((ignorePattern) => url.startsWith(ignorePattern))
? {} ? {}
: options.additionalHeaders; : options.additionalHeaders;
return { return {
method: 'HEAD', method: getHttpMethod(url, options),
headers: { headers: {
...getDefaultHeaders(), ...getDefaultHeaders(url),
...additionalHeaders, ...additionalHeaders,
}, },
redirect: 'manual', // Redirects are handled manually, automatic redirects do not work with Host header
}; };
} }
function getDefaultHeaders(): Record<string, string> { function getHttpMethod(url: string, options: Required<RequestOptions>): 'HEAD' | 'GET' {
if (options.forceHttpGetForUrlPatterns.some((pattern) => url.match(pattern))) {
return 'GET';
}
// By default fetch only headers without the full response body for better speed
return 'HEAD';
}
function getDefaultHeaders(url: string): Record<string, string> {
return { return {
'user-agent': getRandomUserAgent(), // Needed for websites that filter out non-browser user agents.
'upgrade-insecure-requests': '1', 'User-Agent': getRandomUserAgent(),
connection: 'keep-alive',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', // Required for some websites, especially those behind proxies, to correctly handle the request.
'accept-encoding': 'gzip, deflate, br', Host: getDomainFromUrl(url),
'cache-control': 'max-age=0',
'accept-language': 'en-US,en;q=0.9', // The following mimic a real browser request to improve compatibility with most web servers.
'Upgrade-Insecure-Requests': '1',
Connection: 'keep-alive',
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Cache-Control': 'max-age=0',
'Accept-Language': 'en-US,en;q=0.9',
};
}
function getDefaultedRequestOptions(
options?: Partial<RequestOptions>,
): Required<RequestOptions> {
return {
...DefaultOptions,
...options,
}; };
} }

View File

@@ -0,0 +1,69 @@
/**
* Modifies the TLS fingerprint of Node.js HTTP client to circumvent TLS fingerprinting blocks.
* TLS fingerprinting is a technique used to identify clients based on the unencrypted data sent
* during the TLS handshake, used for blocking or identifying non-browser clients like debugging
* proxies or automated scripts.
*
* However, Node.js's HTTP client does not fully support all methods required for impersonating a
* browser's TLS fingerprint, as reported in https://github.com/nodejs/undici/issues/1983.
* While this implementation can alter the TLS fingerprint by randomizing the cipher suite order,
* it may not perfectly mimic specific browser fingerprints due to limitations in the TLS
* implementation of Node.js.
*
* For more detailed information, visit:
* - https://archive.today/2024.03.13-102042/https://httptoolkit.com/blog/tls-fingerprinting-node-js/
* - https://check.ja3.zone/ (To check your tool's or browser's fingerprint)
* - https://github.com/lwthiker/curl-impersonate (A solution for curl)
* - https://github.com/depicts/got-tls (Cipher manipulation support for Node.js)
*/
import { constants } from 'crypto';
import tls from 'tls';
import { indentText } from '@tests/shared/Text';
export function randomizeTlsFingerprint() {
tls.DEFAULT_CIPHERS = getShuffledCiphers().join(':');
console.log(indentText(
`TLS context:\n${indentText([
'Original ciphers:', indentText(constants.defaultCipherList),
'Current ciphers:', indentText(getTlsContextInfo()),
].join('\n'))}`,
));
}
export function getTlsContextInfo(): string {
return [
`Ciphers: ${tls.DEFAULT_CIPHERS}`,
`Minimum TLS protocol version: ${tls.DEFAULT_MIN_VERSION}`,
`Node fingerprint: ${constants.defaultCoreCipherList === tls.DEFAULT_CIPHERS ? 'Visible' : 'Masked'}`,
].join('\n');
}
/**
* Shuffles the order of TLS ciphers, excluding the top 3 most important ciphers to maintain
* security preferences. This approach modifies the default cipher list of Node.js to create a
* unique TLS fingerprint, thus helping to circumvent detection mechanisms based on static
* fingerprinting. It leverages randomness in the cipher order as a simple method to generate a
* new, unique TLS fingerprint which is not easily identifiable. The technique is based on altering
* parameters used in the TLS handshake process, particularly the cipher suite order, to avoid
* matching known fingerprints that could identify the client as a Node.js application.
*
* For more details, refer to:
* - https://archive.today/2024.03.13-102234/https://getsetfetch.org/blog/tls-fingerprint.html
*/
export function getShuffledCiphers(): readonly string[] {
const nodeOrderedCipherList = constants.defaultCoreCipherList.split(':');
const totalTopCiphersToKeep = 3;
// Keep the most important ciphers in the same order
const fixedCiphers = nodeOrderedCipherList.slice(0, totalTopCiphersToKeep);
// Shuffle the rest
const shuffledCiphers = nodeOrderedCipherList.slice(totalTopCiphersToKeep)
.map((cipher) => ({ cipher, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ cipher }) => cipher);
const ciphers = [
...fixedCiphers,
...shuffledCiphers,
];
return ciphers;
}

View File

@@ -2,18 +2,18 @@ export function groupUrlsByDomain(urls: string[]): string[][] {
const domains = new Set<string>(); const domains = new Set<string>();
const urlsWithDomain = urls.map((url) => ({ const urlsWithDomain = urls.map((url) => ({
url, url,
domain: extractDomain(url), domain: getDomainFromUrl(url),
})); }));
for (const url of urlsWithDomain) { for (const url of urlsWithDomain) {
domains.add(url.domain); domains.add(url.domain);
} }
return Array.from(domains).map((domain) => { return Array.from(domains).map((domain) => {
return urlsWithDomain return urlsWithDomain
.filter((url) => url.domain === domain) .filter((url) => url.domain.toLowerCase() === domain.toLowerCase())
.map((url) => url.url); .map((url) => url.url);
}); });
} }
function extractDomain(url: string): string { export function getDomainFromUrl(url: string): string {
return url.split('://')[1].split('/')[0].toLowerCase(); return new URL(url).host;
} }

View File

@@ -0,0 +1,19 @@
import { indentText } from '@tests/shared/Text';
export interface UrlStatus {
readonly url: string;
readonly error?: string;
readonly code?: number;
}
export function formatUrlStatus(status: UrlStatus): string {
return [
`URL: ${status.url}`,
...status.code !== undefined ? [
`Response code: ${status.code}`,
] : [],
...status.error ? [
`Error:\n${indentText(status.error)}`,
] : [],
].join('\n');
}

View File

@@ -3,73 +3,28 @@ export function getRandomUserAgent(): string {
} }
const UserAgents = [ const UserAgents = [
// Chrome // Safari 17.1 - macOS and iPad
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537', // Safari - iOS 17 - iPhone
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
// Firefox // Safari - iOS 17 - iPad mini
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0', 'Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15', // Edge - macOS
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.51',
// Safari // Edge - Windows
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/604.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.58',
// Edge - Android
// Internet Explorer 'Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.43 Mobile Safari/537.36 EdgA/119.0.2151.92',
'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko', // Chrome - macOS
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
// Edge // Chrome - Windows
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3 Edge/15.0', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
// Chrome - Android (Phone)
// Opera 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36',
'Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14', // Firefox - macOS
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/114.0',
// iOS Devices // Firefox - Windows
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/18.2b11866 Mobile/16B91 Safari/605.1.15', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0,',
'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1', // Firefox - Android (Phone)
'Mozilla/5.0 (Android 14; Mobile; rv:109.0) Gecko/120.0 Firefox/120.0',
// Android Devices
'Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.3',
// Other Devices/Browsers
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.3',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15',
'Mozilla/5.0 (Windows Phone 10.0; Android 6.0.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Mobile Safari/537.3 Edge/15.0',
'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0',
'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0',
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.3',
'Mozilla/5.0 (Linux; Android 7.0; SM-G930F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.3',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.3',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1',
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.3 OPR/53.0.2907.99',
'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2)',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:46.0) Gecko/20120121 Firefox/46.0',
'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)',
'Mozilla/5.0 (Windows NT 5.1; rv:36.0) Gecko/20100101 Firefox/36.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0',
'Mozilla/5.0 (X11; Linux i686; rv:30.0) Gecko/20100101 Firefox/30.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:28.0) Gecko/20100101 Firefox/28.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.1; rv:27.3) Gecko/20130101 Firefox/27.3',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0) Gecko/20161202 Firefox/21.0.1',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20130401 Firefox/31.0',
'Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0',
'Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0',
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.3',
'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.3',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.517 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.3',
'Mozilla/5.0 (X11; CrOS x86_64 4319.74.0) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.3',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.3',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.3',
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.3',
]; ];

View File

@@ -1,50 +1,84 @@
import { test, expect } from 'vitest'; import { test, expect } from 'vitest';
import { parseApplication } from '@/application/Parser/ApplicationParser'; import { parseApplication } from '@/application/Parser/ApplicationParser';
import type { IApplication } from '@/domain/IApplication'; import type { IApplication } from '@/domain/IApplication';
import { getUrlStatusesInParallel, type IBatchRequestOptions } from './StatusChecker/BatchStatusChecker'; import { indentText } from '@tests/shared/Text';
import type { IUrlStatus } from './StatusChecker/IUrlStatus'; import { formatAssertionMessage } from '@tests/shared/FormatAssertionMessage';
import { type UrlStatus, formatUrlStatus } from './StatusChecker/UrlStatus';
import { getUrlStatusesInParallel, type BatchRequestOptions } from './StatusChecker/BatchStatusChecker';
// arrange
const app = parseApplication(); const app = parseApplication();
const urls = collectUniqueUrls(app); const urls = collectUniqueUrls({
const requestOptions: IBatchRequestOptions = { application: app,
excludePatterns: [
/^https:\/\/archive\.ph/, // Drops HEAD/GET requests via fetch/curl, responding to Postman/Chromium.
],
});
const requestOptions: BatchRequestOptions = {
domainOptions: { domainOptions: {
sameDomainParallelize: false, // be nice to our external servers sameDomainParallelize: false, // be nice to our third-party servers
sameDomainDelayInMs: 5 /* sec */ * 1000, sameDomainDelayInMs: 5 /* sec */ * 1000,
}, },
requestOptions: { requestOptions: {
retryExponentialBaseInMs: 3 /* sec */ * 1000, retryExponentialBaseInMs: 3 /* sec */ * 1000,
requestTimeoutInMs: 60 /* sec */ * 1000, requestTimeoutInMs: 60 /* sec */ * 1000,
additionalHeaders: { referer: app.projectDetails.homepage }, additionalHeaders: { referer: app.projectDetails.homepage },
randomizeTlsFingerprint: true,
},
followOptions: {
followRedirects: true,
enableCookies: true,
}, },
}; };
const testTimeoutInMs = urls.length * 60 /* seconds */ * 1000; const testTimeoutInMs = urls.length * 60 /* seconds */ * 1000;
test(`all URLs (${urls.length}) should be alive`, async () => { test(`all URLs (${urls.length}) should be alive`, async () => {
// act
const results = await getUrlStatusesInParallel(urls, requestOptions); const results = await getUrlStatusesInParallel(urls, requestOptions);
const deadUrls = results.filter((r) => r.code !== 200); // assert
expect(deadUrls).to.have.lengthOf(0, printUrls(deadUrls)); const deadUrls = results.filter((r) => r.code === undefined || !isOkStatusCode(r.code));
expect(deadUrls).to.have.lengthOf(0, formatAssertionMessage([formatUrlStatusReport(deadUrls)]));
}, testTimeoutInMs); }, testTimeoutInMs);
function collectUniqueUrls(application: IApplication): string[] { function isOkStatusCode(statusCode: number): boolean {
return statusCode >= 200 && statusCode < 300;
}
function collectUniqueUrls(
options: {
readonly application: IApplication,
readonly excludePatterns?: readonly RegExp[],
},
): string[] {
return [ // Get all nodes return [ // Get all nodes
...application.collections.flatMap((c) => c.getAllCategories()), ...options.application.collections.flatMap((c) => c.getAllCategories()),
...application.collections.flatMap((c) => c.getAllScripts()), ...options.application.collections.flatMap((c) => c.getAllScripts()),
] ]
// Get all docs // Get all docs
.flatMap((documentable) => documentable.docs) .flatMap((documentable) => documentable.docs)
// Parse all URLs // Parse all URLs
.flatMap((docString) => docString.match(/(https?:\/\/[^\s`"<>()]+)/g) || []) .flatMap((docString) => extractUrls(docString))
// Remove duplicates // Remove duplicates
.filter((url, index, array) => array.indexOf(url) === index); .filter((url, index, array) => array.indexOf(url) === index)
// Exclude certain URLs based on patterns
.filter((url) => !shouldExcludeUrl(url, options.excludePatterns ?? []));
} }
function printUrls(statuses: IUrlStatus[]): string { function shouldExcludeUrl(url: string, patterns: readonly RegExp[]): boolean {
/* eslint-disable prefer-template */ return patterns.some((pattern) => pattern.test(url));
return '\n' }
+ statuses.map((status) => `- ${status.url}\n`
+ (status.code ? `\tResponse code: ${status.code}` : '') function formatUrlStatusReport(deadUrlStatuses: readonly UrlStatus[]): string {
+ (status.error ? `\tError: ${status.error}` : '')) return `\n${deadUrlStatuses.map((status) => indentText(formatUrlStatus(status))).join('\n---\n')}\n`;
.join('\n') }
+ '\n';
/* eslint-enable prefer-template */ function extractUrls(textWithInlineCode: string): string[] {
/*
Matches URLs:
- Excludes inline code blocks as they may contain URLs not intended for user interaction
and not guaranteed to support expected HTTP methods, leading to false-negatives.
- Supports URLs containing parentheses, avoiding matches within code that might not represent
actual links.
*/
const nonCodeBlockUrlRegex = /(?<!`)(https?:\/\/[^\s`"<>()]+(?:\([^\s`"<>()]*\))?[^\s`"<>()]*)/g;
return textWithInlineCode.match(nonCodeBlockUrlRegex) || [];
} }

View File

@@ -55,7 +55,7 @@ function getPathAliasesFromTsConfig(): ViteAliasDefinitions {
} }
function getElectronProcessSpecificModuleAliases(): ViteAliasDefinitions { function getElectronProcessSpecificModuleAliases(): ViteAliasDefinitions {
// Workaround for scoped Electron module imports due to https://github.com/alex8088/electron-vite/issues/372 // Workaround for Vite not being able to build tests with scoped Electron module imports.
const electronProcessScopedModuleAliases = [ const electronProcessScopedModuleAliases = [
'electron/main', 'electron/main',
'electron/renderer', 'electron/renderer',