From f12417183eb0d4f56fc01a83c7e2957705af099f Mon Sep 17 00:00:00 2001 From: wellwelwel <46850407+wellwelwel@users.noreply.github.com> Date: Tue, 27 Feb 2024 23:18:22 -0300 Subject: [PATCH] fix(security): sanitize path and prevent shell scripts --- README.md | 19 ++++++++-- SECURITY.md | 27 +++++++++++--- src/modules/list-files.ts | 16 +++++++-- src/services/run-test-file.ts | 1 + src/services/run-tests.ts | 4 +-- .../back-to-the-roots/_category_.json | 8 ----- website/docs/examples/beforeEach.mdx | 2 ++ website/docs/index.mdx | 36 ++++++++++++------- website/docs/security.mdx | 26 ++++++++++++++ website/sidebars.ts | 1 + website/src/css/custom.scss | 3 +- 11 files changed, 108 insertions(+), 35 deletions(-) delete mode 100644 website/docs/examples/back-to-the-roots/_category_.json create mode 100644 website/docs/security.mdx diff --git a/README.md b/README.md index 99b4d644..f9798c5a 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ [ci-image]: https://img.shields.io/github/actions/workflow/status/wellwelwel/poku/ci.yml?event=push&style=flat&label=CI&branch=main [ql-url]: https://github.com/wellwelwel/poku/actions/workflows/codeql.yml?query=branch%3Amain [ql-image]: https://img.shields.io/github/actions/workflow/status/wellwelwel/poku/codeql.yml?event=push&style=flat&label=Code%20QL&branch=main +[license-url]: https://github.com/wellwelwel/poku/blob/main/LICENSE +[license-image]: https://img.shields.io/npm/l/poku.svg?maxAge=2592000&color=9c88ff # Poku @@ -23,6 +25,7 @@ [![TypeScript Version][typescript-version-image]][typescript-url] [![GitHub Workflow Status (with event)][ci-image]][ci-url] [![GitHub Workflow Status (with event)][ql-image]][ql-url] +[![License][license-image]][license-url] Enjoying **Poku**? Consider giving him a star โญ๏ธ @@ -184,10 +187,22 @@ I'm continuously working to improve **Poku**. If you've got something interestin ## Acknowledgements -- [**Contributors**](https://github.com/wellwelwel/poku/graphs/contributors) +- [**Contributors**](https://github.com/wellwelwel/poku/graphs/contributors). --- ## Contributing -Please check the [_CONTRIBUTING.md_](./CONTRIBUTING.md) for instructions ๐Ÿš€ +Please check the [**CONTRIBUTING.md**](./CONTRIBUTING.md) for instructions ๐Ÿš€ + +--- + +## License + +Poku is under the [**MIT** License](./LICENSE). + +--- + +## Security Policy + +Please check the [**SECURITY.md**](./SECURITY.md) and the section [**Is Poku Safe?**](https://poku.dev/docs/security) from Documentation. diff --git a/SECURITY.md b/SECURITY.md index 4770e039..bbc54e3e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,5 +1,21 @@ # Security Policy +## Is Poku Safe? + +**Poku** is an open-source project, so you can see both the [Source Code on **GitHub** Repository](https://github.com/wellwelwel/poku) and the [Distribution Code on **NPM**](https://www.npmjs.com/package/poku?activeTab=code). + +### Why does Poku use `child_process`? + +Some test runners use **`eval`**, **Poku** prefers to use **`spawn`** to create a isolated process securely for each test file. + +--- + +## Protective Measures + +See the [**Protective Measures**](https://poku.dev/docs/security#protective-measures) in the documentation. + +--- + ## Supported Versions Currently, security updates will be applied to the following versions of **Poku**: @@ -9,17 +25,18 @@ Currently, security updates will be applied to the following versions of **Poku* | 1.x.x | :white_check_mark: | | 0.x.x | :x: | -## Reporting a Vulnerability +--- -> **Please, give Detailed Reports** +## Reporting a Vulnerability +- Please, give detailed reports - Include steps to reproduce the vulnerability, and if possible, a patch or workaround. - Include the specific version of **Poku** you are using. Please report it privately: https://github.com/wellwelwel/poku/security/advisories. ---- +- Once the issue has been resolved, you will be attributed a part of the report. -> If you wish it, once the issue has been resolved, you will be attributed a part of the report. +--- -Let's keeping **Poku** safe ๐Ÿฉต +Let's keeping **Poku** safe ๐Ÿท diff --git a/src/modules/list-files.ts b/src/modules/list-files.ts index dd041963..b6c8c1c2 100644 --- a/src/modules/list-files.ts +++ b/src/modules/list-files.ts @@ -3,6 +3,16 @@ import fs from 'node:fs'; import path from 'node:path'; import type { Configs } from '../@types/list-files.js'; +export const sanitizePath = (input: string, ensureTarget?: boolean): string => { + const sanitizedPath = input + .replace(/[/\\]+/g, path.sep) // adapting slashes according to OS + .replace(/(\.\.(\/|\\|$))+/g, '') // ensure the current path level + .replace(/[<>:|^?*]+/g, ''); // removing unusual path characters + + // Preventing absolute path access + return ensureTarget ? sanitizedPath.replace(/^[/\\]/, './') : sanitizedPath; +}; + export const escapeRegExp = (string: string) => string.replace(/[.*{}[\]\\]/g, '\\$&'); @@ -15,7 +25,7 @@ export const listFiles = ( files: string[] = [], configs?: Configs ) => { - const currentFiles = fs.readdirSync(dirPath); + const currentFiles = fs.readdirSync(sanitizePath(dirPath)); const defaultRegExp = /\.(test|spec)\./i; const filter: RegExp = (envFilter @@ -31,7 +41,7 @@ export const listFiles = ( : undefined; for (const file of currentFiles) { - const fullPath = path.join(dirPath, file); + const fullPath = sanitizePath(path.join(dirPath, file)); if (/node_modules/.test(fullPath)) continue; if (exclude && exclude.some((regex) => regex.test(fullPath))) continue; @@ -45,4 +55,4 @@ export const listFiles = ( }; export const publicListFiles = (targetDir: string, configs?: Configs) => - listFiles(targetDir, [], configs); + listFiles(sanitizePath(targetDir), [], configs); diff --git a/src/services/run-test-file.ts b/src/services/run-test-file.ts index 1cbf3d33..207aabaf 100644 --- a/src/services/run-test-file.ts +++ b/src/services/run-test-file.ts @@ -95,6 +95,7 @@ export const runTestFile = ( const child = spawn(runtime!, runtimeArguments, { stdio: ['inherit', 'pipe', 'pipe'], + shell: false, env: { ...process.env, FILE: configs?.parallel ? fileRelative : '', diff --git a/src/services/run-tests.ts b/src/services/run-tests.ts index 632d421a..6b8ff3b7 100644 --- a/src/services/run-tests.ts +++ b/src/services/run-tests.ts @@ -3,7 +3,7 @@ import { EOL } from 'node:os'; import path from 'node:path'; import { runner } from '../helpers/runner.js'; import { indentation } from '../helpers/indentation.js'; -import { listFiles } from '../modules/list-files.js'; +import { listFiles, sanitizePath } from '../modules/list-files.js'; import { hr } from '../helpers/hr.js'; import { format } from '../helpers/format.js'; import { runTestFile } from './run-test-file.js'; @@ -20,7 +20,7 @@ export const runTests = async ( configs?: Configs ): Promise => { const cwd = process.cwd(); - const testDir = path.join(cwd, dir); + const testDir = path.join(cwd, sanitizePath(dir)); const currentDir = path.relative(cwd, testDir); const files = listFiles(testDir, undefined, configs); const totalTests = files.length; diff --git a/website/docs/examples/back-to-the-roots/_category_.json b/website/docs/examples/back-to-the-roots/_category_.json deleted file mode 100644 index d2532ad7..00000000 --- a/website/docs/examples/back-to-the-roots/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "Back to the Roots", - "collapsed": false, - "link": { - "type": "generated-index" - }, - "position": 1 -} diff --git a/website/docs/examples/beforeEach.mdx b/website/docs/examples/beforeEach.mdx index 4d01b1da..8d8cab58 100644 --- a/website/docs/examples/beforeEach.mdx +++ b/website/docs/examples/beforeEach.mdx @@ -103,6 +103,8 @@ node ./test/run.test.js > Or `npx tsx ./test/run.test.ts` for **TypeScript**. +
+ :::tip **Poku**'s `assert` will use the message exactly as it is when using `describe` or `it`.
Your **Poku** is waiting for you ๐Ÿทโœจ diff --git a/website/docs/index.mdx b/website/docs/index.mdx index d3ec1463..baf0bc12 100644 --- a/website/docs/index.mdx +++ b/website/docs/index.mdx @@ -51,25 +51,23 @@ Enjoying **Poku**? Consider giving him a star โญ๏ธ ## Why Poku? -Don't worry about `describe`, `it`, `beforeEach` and everything else ๐Ÿš€ - -> You don't need to learn what you already know โœจ -

- Designed to be highly intuitive -

-

- - No constraints or rules, code in your own signature style + You don't need to learn what you already know โœจ

- Compare Poku with the Most Popular Test Runners + + Don't worry about `describe`, `it`, `beforeEach` and everything else ๐Ÿš€ +

+
+ +- [**Compare Poku with the Most Popular Test Runners ๐Ÿงช**](/docs/comparing) +
## **Install** @@ -174,7 +172,7 @@ deno run npm:poku targetDir ## Documentation -> Initially, the [documentation](/docs/category/documentation) is based on **Node.js** usage, but you can use all the options normally for both **Bun** and **Deno**. +> Initially, the [**documentation**](/docs/category/documentation) and [**examples**](/docs/category/examples) are based on **Node.js** usage, but you can use all the options normally for both **Bun** and **Deno**.
@@ -186,10 +184,22 @@ I'm continuously working to improve **Poku**. If you've got something interestin ## Acknowledgements -- [**Contributors**](https://github.com/wellwelwel/poku/graphs/contributors) +- [**Contributors**](https://github.com/wellwelwel/poku/graphs/contributors).
## Contributing -Please check the [_CONTRIBUTING.md_](https://github.com/wellwelwel/poku/blob/main/CONTRIBUTING.md) for instructions ๐Ÿš€ +Please check the [**CONTRIBUTING.md**](https://github.com/wellwelwel/poku/blob/main/CONTRIBUTING.md) for instructions ๐Ÿš€ + +
+ +## License + +Poku is under the [**MIT License**](https://github.com/wellwelwel/poku/blob/main/LICENSE). + +
+ +## Security Policy + +Please check the [**SECURITY.md**](https://github.com/wellwelwel/poku/blob/main/SECURITY.md) and the section [**Is Poku Safe?**](/docs/security) from Documentation. diff --git a/website/docs/security.mdx b/website/docs/security.mdx new file mode 100644 index 00000000..62cdff6c --- /dev/null +++ b/website/docs/security.mdx @@ -0,0 +1,26 @@ +# Is Poku Safe? + +**Poku** is an open-source project, so you can see both the [Source Code on **GitHub** Repository](https://github.com/wellwelwel/poku) and the [Distribution Code on **NPM**](https://www.npmjs.com/package/poku?activeTab=code). + +## Why does Poku use `child_process`? + +Some test runners use **`eval`**, **Poku** prefers to use **`spawn`** to create a isolated process securely for each test file. + +## Protective Measures + +- Blocks access above target directory by filtering `../` and `/` paths, for example: + - `/root` will be sanitized to `./root` + - `../../etc/secret` will be sanitized to `./etc/secret` +- Normalizes paths according to the OS, allowing all collaborators to use the same path, each using their own OS: + - `\` for **Windows** + - `/` for **Linux** and **macOS** +- Normalizes paths by filtering unusual path characters, for example: + - `<>:|^?*` +- Prevents shell scripts by setting `shell` to `false` in **`spawn`** options, ensuring that only secure arguments will be used. +- Every **RegExp** is prev-tested using the [**ReDoS Checker**](https://devina.io/redos-checker). + +## Security Policy + +:::info +See the [**Security Policy** on **GitHub** repository](https://github.com/wellwelwel/poku/blob/main/SECURITY.md). +::: diff --git a/website/sidebars.ts b/website/sidebars.ts index 72033a7c..4270a437 100644 --- a/website/sidebars.ts +++ b/website/sidebars.ts @@ -5,6 +5,7 @@ const sidebars: SidebarsConfig = { 'index', 'comparing', 'overview', + 'security', { type: 'category', label: 'Documentation', diff --git a/website/src/css/custom.scss b/website/src/css/custom.scss index 50bc67e3..b4b77cd0 100644 --- a/website/src/css/custom.scss +++ b/website/src/css/custom.scss @@ -16,7 +16,7 @@ [data-theme] { blockquote { - font-size: 0.95rem; + font-size: 0.875rem; } } @@ -131,7 +131,6 @@ html[data-theme='dark'] { color: #b9bfdc; blockquote { - font-size: 13px; color: #8188ab; }