From bd8c5fc2865b1a7ed2bdebf9a4a02d31f61bf10b Mon Sep 17 00:00:00 2001 From: RonithManikonda Date: Sun, 13 Oct 2024 17:58:16 -0500 Subject: [PATCH 1/6] feat(terminal): restrict default allowed commands to 'ls' and 'echo' Change the default value of `terminal.allowCommands` from an empty array to `['ls', 'echo']`. This change prevents users from running all available commands by default in tutorials, enhancing security and focus. Lesson authors can still allow all commands by specifying `terminal.allowCommands: []` in the metadata. BREAKING CHANGE: The default value of `terminal.allowCommands` is now restricted to `['ls', 'echo']`. To allow all commands, explicitly set `terminal.allowCommands: []` in the metadata. Closes #302 --- .../content/docs/reference/configuration.mdx | 2 +- .../src/webcontainer/terminal-config.ts | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx b/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx index a1c6419a5..6de633cba 100644 --- a/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx +++ b/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx @@ -312,7 +312,7 @@ You can set terminal open by default by specifying the `open` value. An interactive terminal will disable the output redirect syntax by default. For instance, you cannot create a file `world.txt` with the contents `hello` using the command `echo hello > world.txt`. The reason is that this could disrupt the lesson if a user overwrites certain files. To allow output redirection, you can change the behavior with the `allowRedirects` setting. You can define this setting either per panel or for all panels at once. -Additionally, you may not want users to run arbitrary commands. For example, if you are creating a lesson about `vitest`, you could specify that the only command the user can run is `vitest` by providing a list of `allowCommands`. Any other command executed by the user will be blocked. You can define the `allowCommands` setting either per panel or for all panels at once. +Additionally, you may not want users to run arbitrary commands. For example, if you are creating a lesson about vitest, you could specify that the only command the user can run is vitest by providing a list of allowCommands. By default, the allowed commands are ls and echo. Providing a list of allowCommands will override these defaults, and specifying an empty list will allow all commands. Any other command executed by the user will be blocked. You can define the allowCommands setting either per panel or for all panels at once. By default, in every new lesson terminals start a new session. If you want to keep the terminal session between lessons, you can specify the `id` property for a given terminal panel and keep the same `id` across lessons. diff --git a/packages/runtime/src/webcontainer/terminal-config.ts b/packages/runtime/src/webcontainer/terminal-config.ts index cdfb8afa4..145f0c36a 100644 --- a/packages/runtime/src/webcontainer/terminal-config.ts +++ b/packages/runtime/src/webcontainer/terminal-config.ts @@ -200,6 +200,9 @@ export class TerminalPanel implements ITerminal { } } +// set the default commands for the terminal +const DEFAULT_COMMANDS = ['ls', 'echo']; + /** * Normalize the provided configuration to a configuration which is easier to parse. * @@ -232,9 +235,21 @@ function normalizeTerminalConfig(config?: TerminalSchema): NormalizedTerminalCon const panels: TerminalPanel[] = []; + const resolveAllowCommands = (allowCommands?: string[]): string[] | undefined => { + if (allowCommands === undefined) { + return DEFAULT_COMMANDS; + } + + if (Array.isArray(allowCommands) && allowCommands.length === 0) { + return undefined; + } + + return allowCommands; + }; + const options = { allowRedirects: config.allowRedirects, - allowCommands: config.allowCommands, + allowCommands: resolveAllowCommands(config.allowCommands), }; if (config.panels) { @@ -258,7 +273,7 @@ function normalizeTerminalConfig(config?: TerminalSchema): NormalizedTerminalCon id: panel.id, title: panel.title, allowRedirects: panel.allowRedirects ?? config.allowRedirects, - allowCommands: panel.allowCommands ?? config.allowCommands, + allowCommands: panel.allowCommands ?? DEFAULT_COMMANDS, }); } From 8ba09bd809f671b8aea70c55585a8803f514100c Mon Sep 17 00:00:00 2001 From: RonithManikonda Date: Wed, 30 Oct 2024 22:11:20 -0500 Subject: [PATCH 2/6] test(terminal): update tests to align with default command behavior adjust tests to expect ['ls', 'echo'] as default allowCommands when undefined, aligning tests with updated functionality closes #302 --- .../runtime/src/webcontainer/terminal-config.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/runtime/src/webcontainer/terminal-config.spec.ts b/packages/runtime/src/webcontainer/terminal-config.spec.ts index 8dd2f851e..881cacaa5 100644 --- a/packages/runtime/src/webcontainer/terminal-config.spec.ts +++ b/packages/runtime/src/webcontainer/terminal-config.spec.ts @@ -56,13 +56,13 @@ describe('TerminalConfig', () => { expect(config.panels[0].id).toBe('foo'); expect(config.panels[0].processOptions).toEqual({ allowRedirects: false, - allowCommands: undefined, + allowCommands: ['ls', 'echo'], }); expect(config.panels[1].title).toBe('Bar'); expect(config.panels[1].id).toBe('bar'); expect(config.panels[1].processOptions).toEqual({ allowRedirects: false, - allowCommands: undefined, + allowCommands: ['ls', 'echo'], }); }); @@ -144,13 +144,13 @@ describe('TerminalConfig', () => { expect(config.panels[0].title).toBe('TERM 1'); expect(config.panels[0].processOptions).toEqual({ allowRedirects: true, - allowCommands: ['echo'], + allowCommands: ['ls', 'echo'], }); expect(config.panels[1].title).toBe('TERM 2'); expect(config.panels[1].processOptions).toEqual({ allowRedirects: true, - allowCommands: ['echo'], + allowCommands: ['ls', 'echo'], }); expect(config.panels[2].title).toBe('OUT'); @@ -171,7 +171,7 @@ describe('TerminalConfig', () => { expect(config.panels[0].title).toBe('TERM 1'); expect(config.panels[0].processOptions).toEqual({ allowRedirects: false, - allowCommands: ['echo'], + allowCommands: ['ls', 'echo'], }); expect(config.panels[1].title).toBe('TERM 2'); From bb85dca819191606d360ae5cb5076515855243f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Fri, 1 Nov 2024 11:08:13 +0200 Subject: [PATCH 3/6] docs: keep backticks --- .../src/content/docs/reference/configuration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx b/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx index 6de633cba..9326ef5d3 100644 --- a/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx +++ b/docs/tutorialkit.dev/src/content/docs/reference/configuration.mdx @@ -312,7 +312,7 @@ You can set terminal open by default by specifying the `open` value. An interactive terminal will disable the output redirect syntax by default. For instance, you cannot create a file `world.txt` with the contents `hello` using the command `echo hello > world.txt`. The reason is that this could disrupt the lesson if a user overwrites certain files. To allow output redirection, you can change the behavior with the `allowRedirects` setting. You can define this setting either per panel or for all panels at once. -Additionally, you may not want users to run arbitrary commands. For example, if you are creating a lesson about vitest, you could specify that the only command the user can run is vitest by providing a list of allowCommands. By default, the allowed commands are ls and echo. Providing a list of allowCommands will override these defaults, and specifying an empty list will allow all commands. Any other command executed by the user will be blocked. You can define the allowCommands setting either per panel or for all panels at once. +Additionally, you may not want users to run arbitrary commands. For example, if you are creating a lesson about `vitest`, you could specify that the only command the user can run is `vitest` by providing a list of `allowCommands`. By default, the allowed commands are `ls` and `echo`. Providing a list of `allowCommands` will override these defaults, and specifying an empty list will allow all commands. Any other command executed by the user will be blocked. You can define the `allowCommands` setting either per panel or for all panels at once. By default, in every new lesson terminals start a new session. If you want to keep the terminal session between lessons, you can specify the `id` property for a given terminal panel and keep the same `id` across lessons. From b5a30fb081c6f2b81a5abbaa26ffbffed8b3bbef Mon Sep 17 00:00:00 2001 From: RonithManikonda <111534854+RonithManikonda@users.noreply.github.com> Date: Mon, 4 Nov 2024 23:05:20 -0600 Subject: [PATCH 4/6] Update packages/runtime/src/webcontainer/terminal-config.spec.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ari Perkkiƶ --- packages/runtime/src/webcontainer/terminal-config.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime/src/webcontainer/terminal-config.spec.ts b/packages/runtime/src/webcontainer/terminal-config.spec.ts index 881cacaa5..3ab225d18 100644 --- a/packages/runtime/src/webcontainer/terminal-config.spec.ts +++ b/packages/runtime/src/webcontainer/terminal-config.spec.ts @@ -144,7 +144,7 @@ describe('TerminalConfig', () => { expect(config.panels[0].title).toBe('TERM 1'); expect(config.panels[0].processOptions).toEqual({ allowRedirects: true, - allowCommands: ['ls', 'echo'], + allowCommands: ['echo'], }); expect(config.panels[1].title).toBe('TERM 2'); From 69ec710c7f14f04e10e78892baeedeb60e98445c Mon Sep 17 00:00:00 2001 From: RonithManikonda <111534854+RonithManikonda@users.noreply.github.com> Date: Mon, 4 Nov 2024 23:05:31 -0600 Subject: [PATCH 5/6] Update packages/runtime/src/webcontainer/terminal-config.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ari Perkkiƶ --- packages/runtime/src/webcontainer/terminal-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime/src/webcontainer/terminal-config.ts b/packages/runtime/src/webcontainer/terminal-config.ts index 145f0c36a..04d25c8fa 100644 --- a/packages/runtime/src/webcontainer/terminal-config.ts +++ b/packages/runtime/src/webcontainer/terminal-config.ts @@ -273,7 +273,7 @@ function normalizeTerminalConfig(config?: TerminalSchema): NormalizedTerminalCon id: panel.id, title: panel.title, allowRedirects: panel.allowRedirects ?? config.allowRedirects, - allowCommands: panel.allowCommands ?? DEFAULT_COMMANDS, + allowCommands: panel.allowCommands ? resolveAllowCommands(panel.allowCommands) : options.allowCommands, }); } From 8d0a095edc732998469e085d9d2ffb06dc2645e3 Mon Sep 17 00:00:00 2001 From: RonithManikonda Date: Wed, 15 Jan 2025 16:56:02 -0600 Subject: [PATCH 6/6] feat(terminal): restrict default allowed commands to 'ls' and 'echo' Change the default value of `terminal.allowCommands` from an empty array to `['ls', 'echo']`. This update enhances security and focus by limiting the commands available in tutorials by default. Lesson authors can still enable all commands by setting `terminal.allowCommands: []` explicitly in the metadata. BREAKING CHANGE: The default value of `terminal.allowCommands` is now restricted to `['ls', 'echo']`. To allow all commands, explicitly set `terminal.allowCommands: []` in the metadata. Closes #372 --- .../src/webcontainer/terminal-config.spec.ts | 4 ++-- .../runtime/src/webcontainer/terminal-config.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/runtime/src/webcontainer/terminal-config.spec.ts b/packages/runtime/src/webcontainer/terminal-config.spec.ts index 3ab225d18..ae5a4a774 100644 --- a/packages/runtime/src/webcontainer/terminal-config.spec.ts +++ b/packages/runtime/src/webcontainer/terminal-config.spec.ts @@ -150,7 +150,7 @@ describe('TerminalConfig', () => { expect(config.panels[1].title).toBe('TERM 2'); expect(config.panels[1].processOptions).toEqual({ allowRedirects: true, - allowCommands: ['ls', 'echo'], + allowCommands: ['echo'], }); expect(config.panels[2].title).toBe('OUT'); @@ -171,7 +171,7 @@ describe('TerminalConfig', () => { expect(config.panels[0].title).toBe('TERM 1'); expect(config.panels[0].processOptions).toEqual({ allowRedirects: false, - allowCommands: ['ls', 'echo'], + allowCommands: ['echo'], }); expect(config.panels[1].title).toBe('TERM 2'); diff --git a/packages/runtime/src/webcontainer/terminal-config.ts b/packages/runtime/src/webcontainer/terminal-config.ts index 04d25c8fa..4704005e8 100644 --- a/packages/runtime/src/webcontainer/terminal-config.ts +++ b/packages/runtime/src/webcontainer/terminal-config.ts @@ -235,21 +235,21 @@ function normalizeTerminalConfig(config?: TerminalSchema): NormalizedTerminalCon const panels: TerminalPanel[] = []; - const resolveAllowCommands = (allowCommands?: string[]): string[] | undefined => { - if (allowCommands === undefined) { - return DEFAULT_COMMANDS; + const resolveAllowCommands = (globalCommands?: string[], panelCommands?: string[]): string[] | undefined => { + if (panelCommands === undefined) { + return globalCommands ?? DEFAULT_COMMANDS; } - if (Array.isArray(allowCommands) && allowCommands.length === 0) { + if (Array.isArray(panelCommands) && panelCommands.length === 0) { return undefined; } - return allowCommands; + return panelCommands; }; const options = { allowRedirects: config.allowRedirects, - allowCommands: resolveAllowCommands(config.allowCommands), + allowCommands: config.allowCommands, }; if (config.panels) { @@ -273,7 +273,7 @@ function normalizeTerminalConfig(config?: TerminalSchema): NormalizedTerminalCon id: panel.id, title: panel.title, allowRedirects: panel.allowRedirects ?? config.allowRedirects, - allowCommands: panel.allowCommands ? resolveAllowCommands(panel.allowCommands) : options.allowCommands, + allowCommands: resolveAllowCommands(config.allowCommands, panel.allowCommands), }); }