This repository contains the files for the itunderground.dk website. The main branch has the source, while the gh-pages branch is the live static page.
The website itself is a simulation of a Linux shell in the browser, made using SvelteKit. It also supposed blog-like contents in the form of markdown files, located in src/routes/post/.
There are two user-friendly ways to extend the website. Markdown files and commands.
To extend, you first need to set up a local development environment.
- Git: https://git-scm.com/downloads
- Node.js: https://nodejs.org/ (Most newer versions should work, project was last built with v20).
- pnpm: https://pnpm.io/installation (you can probably use npm if you change
pnpmtonpminpackage.json.)
Clone this repository to your machine:
git clone https://github.com/ITUnderground/itunderground.dkOnce it's been cloned, enter it and install the npm packages.
cd itunderground.dk
pnpm install # Note: You can also choose to use npm, but pnpm is preferredTo run the website locally use following command:
pnpm run devIt will host the website on localhost:5173.
Once you're confident with your changes, push them to GitHub and the Actions Workflow will automatically update the site:
git add .
git commit -m "Write what you changed here"
git push❗ Warning
Pushing to themasterbranch will immediately update the LIVE version on itunderground.dk, so make sure all your changes are final before pushing.
Once you've set up a local environment, you can begin updating the website.
There are 3 main elements of itunderground.dk:
Markdown files are used for blog-like posts. An example of this is itunderground.dk/post/who-are-we. The corresponding markdown file is located in src/routes/post/who-are-we/+page.md.
To render markdown, we use mdsvex. It supports both .md files and .svx files, and both support directly inserting Svelte components. That means you can for example create a dynamic markdown file that fetches data from some server (remember, the site is static so make sure any data fetching happens client-side!).
Dynamic data fetching in markdown components has not yet been used on our site, but feel free to add it to the readme if you decide to utilize it.
To add a markdown file, create a directory corresponding to its name and add a +page.md file. Remember to link to the file somewhere, such as in the default filesystem or there won't be a way to navigate to it (This is not necessary for writeups unless starting a new CTF. Their links are automatically updated).
Push your changes to GitHub to see the updated website.
All commands in the Linux shell are custom implementations written in TypeScript. They're located in src/lib/shell/commands/, with some built-in core commands located in builtin/.
To add a command create a new TypeScript file in the commands/ directory with the name of the command you want to implement. Copy the following template:
import Command from '../command';
export default new Command({
command({ cli, dir, env, js, command }) {
// Logic here
const flagValue = command.namedArguments['userflag'];
cli.stdout('Hello world!');
return `You passed the flag ${flagValue}!`;
},
description: 'My cool and awesome command.',
namedArguments: [
{
name: 'userflag',
choices: ['flag', 'userflag', 'f'],
hasValue: true
}
]
});It is highly recommanded that you use something like IntelliSense to view all properties on the AccesObject that your command has access to. It can also be helpful to look at the type definitions in src/lib/shell/types.d.ts. Methods starting with and underscore (_) should be avoided as they are meant for internal use. See other commands for inspiration on how to make use of this object.
- The AccessObject contains all the metadata you have access to in the command.
cli: Exposes basic CLI methods that allows you to run a command and print to regular output, as well as the log.
Seeclearandsudofor commands that use this object.dir: Exposes directory related methods, like current working directory and methods to get directories and files.
Seecdandlsfor commands that use this object.env: Exposes methods related to environment variables, likegetandset.
Seebuiltin/echofor a command that uses this object.js: Allow you to execute JavaScript in the browser.
Seeneofetchfor a command that uses this function.command: Information about the command that was run. This object contains the arguments of the command.
nameis the name of the command, aka. the first word of the input.positionalArgumentsare arguments relative to a command. That is, incat file.txt,file.txtwould be the first positional argument. This is a list of strings.
namedArgumentsare arguments passed as flags. That is, inpython -m script,scriptis the value of the named argument-m. To receivenamedArguments, they must be defined in thenamedArgumentssection. In the example above, the argument named "userflag" can be passed to the command using either--flag,--userflagor-f. Internally, its name is "userflag", and it expects a value. IfhasValueis nullable, the value of the argument will simply betrueif it is present. The value of any argument not passed by the user isfalse.
Note: If an argument withhasValue: trueis passed to the command without a value, it will have valueundefined. If the argument is not passed to the command, it will have valuefalse.rawis the raw string command.
Seecatandsudofor commands that implement this object.
- There are 2 ways to write output from the command. The first is to use
cli.stdout(), the second is to return a string. The latter is preferred. The command does not have to return anything, but if it does it this should be a string (or a promise of a string/void).
Note that any HTML in bothcli.stdoutand return value will be rendered, though script tag will not execute (see for exampleneofetch). - Commands can be asynchronous and return promises. Note that async commands are currently blocking. See
neofetchfor async usage.
Finally, to be able to use the command, add it in commands/index.ts. Commands not defined in this file will not work.