diff --git a/src/@types/poku.ts b/src/@types/poku.ts index 34718d79..3bc16021 100644 --- a/src/@types/poku.ts +++ b/src/@types/poku.ts @@ -2,6 +2,11 @@ import type { Configs as ListFilesConfigs } from './list-files.js'; +export type DenoOptions = { + allow?: string[]; + deny?: string[]; +}; + export type Configs = { /** * By setting `true`, **Poku** won't exit the process and will return the exit code (`0` or `1`). @@ -93,6 +98,7 @@ export type Configs = { * ``` */ afterEach?: () => unknown | Promise; + deno?: DenoOptions; } & ListFilesConfigs; /* c8 ignore stop */ diff --git a/src/bin/index.ts b/src/bin/index.ts index 41821c49..9265f123 100644 --- a/src/bin/index.ts +++ b/src/bin/index.ts @@ -3,11 +3,18 @@ /* c8 ignore start */ import { escapeRegExp } from '../modules/list-files.js'; -import { getArg, getLastParam, hasArg } from '../helpers/get-arg.js'; +import { + // getAllArgs, + getArg, + getLastParam, + hasArg, + getSubArg, +} from '../helpers/get-arg.js'; import { kill, poku } from '../index.js'; import { platformIsValid } from '../helpers/get-runtime.js'; import { format } from '../helpers/format.js'; +// Argument with values const dirs = (hasArg('include') ? getArg('include')?.split(',') @@ -19,6 +26,13 @@ const killPort = getArg('kill-port'); const killRange = getArg('kill-range'); const killPID = getArg('kill-pid'); const concurrency = Number(getArg('concurrency')) || undefined; +const denoAllow = getSubArg('deno-allow'); + +// Multiple arguments with values or not +// TODO (Custom Args) +// const args = getAllArgs('arg'); + +// Argument exists const parallel = hasArg('parallel'); const quiet = hasArg('quiet'); const debug = hasArg('debug'); @@ -64,6 +78,11 @@ if (hasArg('log-success')) debug, failFast, concurrency, + // TODO (Custom Args) + // arguments: args.length > 0 ? args : undefined, + deno: { + allow: denoAllow, + }, }); })(); diff --git a/src/helpers/get-arg.ts b/src/helpers/get-arg.ts index 80ff90c8..70c4d70c 100644 --- a/src/helpers/get-arg.ts +++ b/src/helpers/get-arg.ts @@ -1,19 +1,116 @@ /* c8 ignore start */ import process from 'node:process'; -const [, , ...args] = process.argv; +const [, , ...processArgs] = process.argv; -export const getArg = (arg: string): string | undefined => { - const getArg = args.find((a) => a.startsWith(`--${arg}=`)); - if (getArg) return getArg.split('=')?.[1] || undefined; +/** + * Gets the value of an argument. + * + * --- + * + * CLI arguments examples: + * + * ```sh + * command --arg=some # 'some' + * command --arg="" # '' + * command --arg # undefined + * ``` + */ +export const getArg = (arg: string, prefix = '--'): string | undefined => { + const mountArg = processArgs.find((a) => a.startsWith(`${prefix}${arg}=`)); + if (!mountArg) return undefined; - return undefined; + return mountArg.split('=')?.[1].replace(/''|""/, ''); }; -export const hasArg = (arg: string): boolean => - args.some((a) => a.startsWith(`--${arg}`)); +/** + * Parses all arguments of an argument value. + * + * --- + * + * CLI arguments examples: + * + * ```sh + * command --arg='--sub=some' # ['--sub=some'] + * command --arg='--sub=some, --sub2' # ['--sub=some', '--sub2'] + * ``` + */ +export const getSubArg = (arg: string, prefix = '--') => { + if (hasArg(arg) && !getArg(arg)?.[1]) return []; -export const getLastParam = (): string => { - return args[args.length - 1]; + return processArgs + .find((a) => a.startsWith(`${prefix}${arg}=`)) + ?.split(`--${arg}=`)[1] + .split(',') + .map((a) => a.trim()) + .filter((a) => a && !/''|""/.test(a)); }; + +/** + * Checks if an argument exists. + * + * --- + * + * CLI arguments examples: + * + * ```sh + * command --arg # true + * command # false + * ``` + */ +export const hasArg = (arg: string, prefix = '--'): boolean => + processArgs.some((a) => a.startsWith(`${prefix}${arg}`)); + +/** + * Gets the last param/value. + * + * CLI arguments examples: + * + * ```sh + * command --arg --arg2=some value # 'value' + * command value # 'value' + * command # undefined + * command --arg # undefined + * ``` + */ +export const getLastParam = (prefix = '--'): string | undefined => { + const lastArg = processArgs[processArgs.length - 1]; + + if (!lastArg || lastArg.startsWith(prefix)) return undefined; + + return lastArg; +}; + +// TODO (Custom Args) +// export const getAllArgs = (arg: string, prefix = '--'): string[] => { +// return processArgs +// .filter((a) => a.startsWith(`${prefix}${arg}=`) || a === `${prefix}${arg}`) +// .map((a) => { +// const [key, ...value] = a.split('='); +// return value.length > 0 ? value.join('=') : key; +// }); +// }; + +// TODO (Custom Args) +// export const setArgs = ( +// args: (string | Record)[], +// options?: { prefix: string } +// ): string[] => { +// const customArgs: string[] = []; +// const prefix = options?.prefix || ''; + +// args.forEach((arg) => { +// if (!Array.isArray(arg) && typeof arg === 'object') { +// for (const key in arg) { +// customArgs.push(`${prefix}${key}=${arg[key]}`); +// } + +// return; +// } + +// customArgs.push(`${prefix}${arg}`); +// }); + +// return customArgs; +// }; /* c8 ignore stop */ diff --git a/src/helpers/runner.ts b/src/helpers/runner.ts index 22324954..8eb66527 100644 --- a/src/helpers/runner.ts +++ b/src/helpers/runner.ts @@ -15,15 +15,21 @@ export const runner = (filename: string, configs?: Configs): string[] => { if (runtime === 'bun') return ['bun']; // Deno - if (runtime === 'deno') - return [ - 'deno', - 'run', - '--allow-read', // Poku searches for all test files - '--allow-env', // Poku share the process.env with the `child_process` - '--allow-run', // Poku CLI - '--allow-net', // Create Service - ]; + if (runtime === 'deno') { + const denoAllow = configs?.deno?.allow + ? configs.deno.allow + .map((allow) => (allow ? `--allow-${allow}` : '')) + .filter((allow) => allow) + : [ + // Defaults + '--allow-read', // Poku searches for all test files + '--allow-env', // Poku share the process.env with the `child_process` + '--allow-run', // Poku CLI + '--allow-net', // Create Service + ]; + + return ['deno', 'run', ...denoAllow]; + } // Node.js return path.extname(filename) === '.ts' diff --git a/test/unit/deno/allow.test.ts b/test/unit/deno/allow.test.ts new file mode 100644 index 00000000..6a31f54c --- /dev/null +++ b/test/unit/deno/allow.test.ts @@ -0,0 +1,65 @@ +import { assert, describe, test } from '../../../src/index.js'; +import { runner } from '../../../src/helpers/runner.js'; + +describe('Deno Security Arguments', { background: false, icon: '🔬' }); + +test(() => { + assert.deepStrictEqual( + runner('', { + platform: 'deno', + }), + [ + 'deno', + 'run', + '--allow-read', + '--allow-env', + '--allow-run', + '--allow-net', + ], + 'Default Permissions' + ); + + assert.deepStrictEqual( + runner('', { + platform: 'deno', + deno: { + allow: ['read'], + }, + }), + ['deno', 'run', '--allow-read'], + 'Custom Permission' + ); + + assert.deepStrictEqual( + runner('', { + platform: 'deno', + deno: { + allow: ['read', 'env'], + }, + }), + ['deno', 'run', '--allow-read', '--allow-env'], + 'Custom Permissions' + ); + + assert.deepStrictEqual( + runner('', { + platform: 'deno', + deno: { + allow: ['read="file.js"', 'env'], + }, + }), + ['deno', 'run', '--allow-read="file.js"', '--allow-env'], + 'Custom Permissions per Files' + ); + + assert.deepStrictEqual( + runner('', { + platform: 'deno', + deno: { + allow: [], + }, + }), + ['deno', 'run'], + 'No Permissions' + ); +}); diff --git a/tools/workflows/get-prs.ts b/tools/workflows/get-prs.ts deleted file mode 100644 index 4cf4eb1f..00000000 --- a/tools/workflows/get-prs.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getArg } from '../../src/helpers/get-arg.js'; - -type PR = { - title?: string; -}; - -(async () => { - const PRs: PR[] = await ( - await fetch('https://api.github.com/repos/wellwelwel/poku/pulls?state=open') - ) - .clone() - .json(); - - const titles = PRs.map((PR) => String(PR?.title)); - const title = String(getArg('title')); - - process.exit(titles.includes(title) ? 0 : 1); -})(); diff --git a/website/docs/documentation/poku/options/deno.mdx b/website/docs/documentation/poku/options/deno.mdx new file mode 100644 index 00000000..16c6efb5 --- /dev/null +++ b/website/docs/documentation/poku/options/deno.mdx @@ -0,0 +1,59 @@ +--- +sidebar_position: 98 +--- + +# `deno` + +## `allow` + +> `poku(targetPaths: string | string[], configs?: Configs)` +> +> `allow: string[]` + +Change permissions for **Deno**. + +By default **Poku** uses `--allow-run`, `--allow-env`, `--allow-read` and `--allow-net`. + +### API (_in-code_) + +```ts +poku(['...'], { + deno: { + allow: ['read', 'run' /* ... */], + }, +}); +``` + +```ts +poku(['...'], { + deno: { + allow: ['read=file.js', 'run' /* ... */], + }, +}); +``` + +Clear all permissions: + +```ts +poku(['...'], { + deno: { + allow: [], + }, +}); +``` + +### CLI + +```bash +npx poku --deno-allow='read, run' ./test +``` + +```bash +npx poku --deno-allow='read=file.js, run' ./test +``` + +Clear all permissions: + +```bash +npx poku --deno-allow='' ./test +```