├── .gitignore ├── images ├── setup ├── modsecurity.conf ├── ATO.service ├── parse_env ├── ATO ├── uninstall ├── overlayfs_genfstab ├── nginx.conf ├── setup └── setup.ubuntu ├── frontend ├── buildId.d.ts ├── .eslintignore ├── public │ ├── Apl385.ttf │ ├── Apl385.woff │ ├── favicon.ico │ ├── Apl385.woff2 │ ├── security.txt │ ├── .well-known │ │ └── security.txt │ └── vercel.svg ├── postcss.config.js ├── lib │ ├── stringLength.ts │ ├── encoding.ts │ ├── useSystemThemePreference.ts │ ├── store.ts │ ├── urls.ts │ └── api.ts ├── pages │ ├── about.tsx │ ├── index.tsx │ ├── _app.tsx │ ├── legal.tsx │ ├── preferences.tsx │ └── run.tsx ├── next-env.d.ts ├── next.config.js ├── tailwind.config.js ├── .gitignore ├── README.md ├── components │ ├── notification.tsx │ ├── navbar.tsx │ ├── footer.tsx │ ├── resizeableText.tsx │ ├── argvList.tsx │ ├── collapsibleText.tsx │ ├── languageSelector.tsx │ └── about.tsx ├── .eslintrc.js ├── tsconfig.json ├── package.json └── styles │ └── ATO.css ├── runners └── dyalog_apl ├── server.go ├── go.mod ├── ato ├── languages.go ├── api.go └── invocation.go ├── README.md ├── docs ├── security.md ├── installation.md ├── architecture.md ├── contributing.md └── api.md ├── go.sum ├── yargs.c ├── sandbox ├── wrapper.c └── LICENCE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /images: -------------------------------------------------------------------------------- 1 | dyalog/dyalog 2 | -------------------------------------------------------------------------------- /setup/modsecurity.conf: -------------------------------------------------------------------------------- 1 | # TODO 2 | -------------------------------------------------------------------------------- /frontend/buildId.d.ts: -------------------------------------------------------------------------------- 1 | declare const buildId: string; 2 | -------------------------------------------------------------------------------- /frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | public 4 | out 5 | -------------------------------------------------------------------------------- /frontend/public/Apl385.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/dyalog.run/main/frontend/public/Apl385.ttf -------------------------------------------------------------------------------- /frontend/public/Apl385.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/dyalog.run/main/frontend/public/Apl385.woff -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/dyalog.run/main/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/Apl385.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/dyalog.run/main/frontend/public/Apl385.woff2 -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /frontend/lib/stringLength.ts: -------------------------------------------------------------------------------- 1 | export default function stringLength(string: string): number { 2 | return [...string].length; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/pages/about.tsx: -------------------------------------------------------------------------------- 1 | import About from 'components/about'; 2 | 3 | export default function Index() { 4 | return About({ enableRedirect: false }); 5 | } 6 | -------------------------------------------------------------------------------- /frontend/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import About from 'components/about'; 2 | 3 | export default function Index() { 4 | return About({ enableRedirect: true }); 5 | } 6 | -------------------------------------------------------------------------------- /runners/dyalog_apl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd /ATO/context 4 | /ATO/yargs %1 /ATO/options /ATO/yargs %2 /ATO/arguments dyalogscript %1 /ATO/code %2 < /ATO/input 5 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/attempt-this-online/attempt-this-online/ato" 5 | ) 6 | 7 | func main() { 8 | ato.ServerMain() 9 | } 10 | -------------------------------------------------------------------------------- /frontend/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/attempt-this-online/attempt-this-online 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/gorilla/websocket v1.5.0 7 | github.com/rs/cors v1.8.2 8 | github.com/vmihailenco/msgpack/v5 v5.3.5 9 | ) 10 | 11 | require github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 12 | -------------------------------------------------------------------------------- /setup/ATO.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Attempt This Online, a clone of Try It Online 3 | 4 | [Service] 5 | User=ato 6 | ExecStart=+/usr/local/bin/ATO 7 | Restart=on-failure 8 | StartLimitBurst=3 9 | StartLimitInterval=60s 10 | TimeoutStopSec=5s 11 | Delegate=true 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /setup/parse_env: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import json 3 | import sys 4 | 5 | for string in json.load(sys.stdin)["Env"]: 6 | # Put environment variables from `docker inspect` into the format for `yargs` to pass to `bwrap` to set environment 7 | # variables in the container 8 | key, value = string.split("=", maxsplit=1) 9 | sys.stdout.write(f"--setenv\0{key}\0{value}\0") 10 | -------------------------------------------------------------------------------- /frontend/next.config.js: -------------------------------------------------------------------------------- 1 | const nextBuildId = require('next-build-id'); 2 | 3 | module.exports = { 4 | generateBuildId: () => nextBuildId({ dir: __dirname, describe: true }), 5 | webpack: (config, { 6 | buildId, webpack, 7 | }) => { 8 | config.plugins.push(new webpack.DefinePlugin({ buildId: JSON.stringify(buildId) })); 9 | return config; 10 | }, 11 | future: { 12 | webpack5: true, 13 | }, 14 | eslint: { 15 | ignoreDuringBuilds: true, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const defaultTheme = require('tailwindcss/defaultTheme'); 2 | 3 | module.exports = { 4 | content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 5 | darkMode: 'class', 6 | theme: { 7 | extend: { 8 | fontFamily: { 9 | sans: ['Cantarell', 'Fira Sans', 'Roboto', 'Ubuntu', ...defaultTheme.fontFamily.mono], 10 | mono: ['Fira Code', ...defaultTheme.fontFamily.mono], 11 | }, 12 | }, 13 | }, 14 | plugins: [], 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | tsconfig.tsbuildinfo 37 | -------------------------------------------------------------------------------- /setup/ATO: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # Start script for the Attempt This Online server - to be run as root 3 | 4 | base_cg=/sys/fs/cgroup/system.slice/ATO.service 5 | mkdir -p "$base_cg/server" 6 | # move self into a subtree of ATO.service, because otherwise ATO.service itself cannot be configured properly. See https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#no-internal-process-constraint 7 | echo "$$" > "$base_cg/server/cgroup.procs" 8 | echo +memory > "$base_cg/cgroup.subtree_control" 9 | 10 | mkdir -p /run/ATO 11 | chown ato:ato /run/ATO 12 | chmod 775 /run/ATO 13 | sudo -u ato /usr/local/lib/ATO/server 14 | -------------------------------------------------------------------------------- /frontend/lib/encoding.ts: -------------------------------------------------------------------------------- 1 | import { Base64 } from 'js-base64'; 2 | 3 | const ENCODERS: Record Uint8Array)> = { 4 | 'utf-8': s => new TextEncoder().encode(s), 5 | // sbcs is still just UTF-8, but with different byte counting (switching to a different code page, 6 | // if necessary, is done on the backend) 7 | sbcs: s => new TextEncoder().encode(s), 8 | base64: s => Base64.toUint8Array(s), 9 | }; 10 | 11 | const DECODERS: Record string)> = { 12 | 'utf-8': b => new TextDecoder().decode(b), 13 | base64: b => Base64.fromUint8Array(b), 14 | }; 15 | 16 | export { ENCODERS, DECODERS }; 17 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | ``` 10 | 11 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 12 | 13 | For development, create a `.env.local` file in this directory, and set the URL to the API. For basic testing, you can 14 | just use the production ATO instance. 15 | 16 | ``` 17 | # .env.local 18 | NEXT_PUBLIC_ATO_BASE_URL="https://ato.pxeger.com" 19 | ``` 20 | -------------------------------------------------------------------------------- /setup/uninstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | systemctl disable --now ATO.service nginx.service 4 | sed -i '/^overlay/d' /etc/fstab 5 | sed -i '/certbot renew && systemctl reload nginx/d' /var/spool/cron/root 6 | umount /usr/local/lib/ATO/rootfs/* 7 | userdel ato 8 | rm -rf \ 9 | /var/cache/ATO \ 10 | /var/lib/ATO_home \ 11 | /usr/local/lib/ATO \ 12 | /usr/local/share/ATO \ 13 | /usr/local/bin/ATO \ 14 | /usr/local/bin/ATO_yargs \ 15 | /usr/local/bin/ATO_bash \ 16 | /usr/local/bin/ATO_rm \ 17 | /usr/local/bin/ATO_wrapper \ 18 | /usr/local/bin/ATO_sandbox \ 19 | /usr/local/lib/systemd/system/ATO.service \ 20 | /usr/local/lib/systemd/system/ATO.socket 21 | -------------------------------------------------------------------------------- /frontend/components/notification.tsx: -------------------------------------------------------------------------------- 1 | import { XIcon } from '@heroicons/react/solid'; 2 | 3 | export default function Notification( 4 | { children, onClick }: { children: React.ReactNode, onClick: () => void }, 5 | ) { 6 | return ( 7 |
8 |
9 | {children} 10 |
11 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /ato/languages.go: -------------------------------------------------------------------------------- 1 | package ato 2 | 3 | import "github.com/vmihailenco/msgpack/v5" 4 | 5 | type language struct { 6 | Name string `msgpack:"name"` 7 | Image string `msgpack:"image"` 8 | Version string `msgpack:"version"` 9 | Url string `msgpack:"url"` 10 | Sbcs bool `msgpack:"sbcs"` 11 | SE_class string `msgpack:"SE_class"` 12 | } 13 | 14 | var serialisedLanguages []byte 15 | 16 | func init() { 17 | b, err := msgpack.Marshal(Languages) 18 | if err != nil { 19 | panic(err) 20 | } 21 | serialisedLanguages = b 22 | } 23 | 24 | var Languages = map[string]language{ 25 | "dyalog_apl": { 26 | Name: "APL (Dyalog APL)", 27 | Image: "dyalog/dyalog", 28 | Version: "Latest", 29 | Url: "https://www.dyalog.com/products.htm", 30 | Sbcs: false, 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | parserOptions: { project: './tsconfig.json' }, 5 | plugins: [ 6 | '@typescript-eslint', 7 | ], 8 | extends: [ 9 | 'airbnb-typescript', 10 | ], 11 | rules: { 12 | // https://github.com/vercel/next.js/issues/5533 13 | 'jsx-a11y/anchor-is-valid': 'off', 14 | 'no-console': ['error', { allow: ['warn', 'error'] }], 15 | // if you don't use these rules you're dangerous and must be locked up 16 | 'comma-dangle': ['error', 'always-multiline'], 17 | curly: ['error', 'all'], 18 | 'space-before-function-paren': ['error', { anonymous: 'always', named: 'never', asyncArrow: 'always' }], 19 | 'react/react-in-jsx-scope': 'off', 20 | 'arrow-parens': ['error', 'as-needed'], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /frontend/components/navbar.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { AdjustmentsIcon, HomeIcon } from '@heroicons/react/outline'; 3 | 4 | export default function Navbar() { 5 | return ( 6 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/public/security.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNED MESSAGE----- 2 | Hash: SHA512 3 | 4 | Contact: https://www.pxeger.com/#contact-me 5 | Expires: 2022-06-26T23:00:00.000Z 6 | Encryption: https://www.pxeger.com/pgp.asc 7 | Preferred-Languages: en 8 | Canonical: https://ato.pxeger.com/.well-known/security.txt 9 | Policy: https://github.com/attempt-this-online/attempt-this-online/security/policy 10 | -----BEGIN PGP SIGNATURE----- 11 | 12 | iQEzBAEBCgAdFiEEITkSoL/FmAaNQ5qtVq1ayvBwpkAFAmDXPxwACgkQVq1ayvBw 13 | pkBAJgf/XQes/3/27fUcGFiT7KGATkVBczobgCcXbCRdjYG9rosmVKu1Z8N+2dk3 14 | rvFNLZ3WRhp2RMUc6JMeaxal62y3apwOFS+vBk8q6uXH4i/ELAQQB/AJuAJtMkbU 15 | UzNABmocb0NJIve8rASPgVQhnV85UEt9SBPdyzmU9XuFk1imvroRxZod5AISVb35 16 | Q0YID8Q5evErdZpWPHUmTDxyzVLnbnAzDDbl77yrCMIyTjs6sXkYttxGQi4R1Us6 17 | xWUpqI2EXlUQK3Tggqee3RmFrUGhbL6Y32Eo8DgTreCd5WN2ESmnDuuzya4jjKyt 18 | zivOjphIbB7+90G0x2HSA5AqdubjrQ== 19 | =A3Ty 20 | -----END PGP SIGNATURE----- 21 | -------------------------------------------------------------------------------- /frontend/public/.well-known/security.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNED MESSAGE----- 2 | Hash: SHA512 3 | 4 | Contact: https://www.pxeger.com/#contact-me 5 | Expires: 2022-06-26T23:00:00.000Z 6 | Encryption: https://www.pxeger.com/pgp.asc 7 | Preferred-Languages: en 8 | Canonical: https://ato.pxeger.com/.well-known/security.txt 9 | Policy: https://github.com/attempt-this-online/attempt-this-online/security/policy 10 | -----BEGIN PGP SIGNATURE----- 11 | 12 | iQEzBAEBCgAdFiEEITkSoL/FmAaNQ5qtVq1ayvBwpkAFAmDXPxwACgkQVq1ayvBw 13 | pkBAJgf/XQes/3/27fUcGFiT7KGATkVBczobgCcXbCRdjYG9rosmVKu1Z8N+2dk3 14 | rvFNLZ3WRhp2RMUc6JMeaxal62y3apwOFS+vBk8q6uXH4i/ELAQQB/AJuAJtMkbU 15 | UzNABmocb0NJIve8rASPgVQhnV85UEt9SBPdyzmU9XuFk1imvroRxZod5AISVb35 16 | Q0YID8Q5evErdZpWPHUmTDxyzVLnbnAzDDbl77yrCMIyTjs6sXkYttxGQi4R1Us6 17 | xWUpqI2EXlUQK3Tggqee3RmFrUGhbL6Y32Eo8DgTreCd5WN2ESmnDuuzya4jjKyt 18 | zivOjphIbB7+90G0x2HSA5AqdubjrQ== 19 | =A3Ty 20 | -----END PGP SIGNATURE----- 21 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "allowJs": true, 5 | "jsx": "preserve", 6 | "forceConsistentCasingInFileNames": true, 7 | "noImplicitReturns": true, 8 | "noImplicitThis": true, 9 | "noImplicitAny": true, 10 | "strictNullChecks": true, 11 | "suppressImplicitAnyIndexErrors": true, 12 | "noUnusedLocals": true, 13 | "skipLibCheck": true, 14 | "target": "es5", 15 | "lib": [ 16 | "dom", 17 | "dom.iterable", 18 | "esnext" 19 | ], 20 | "strict": false, 21 | "noEmit": true, 22 | "esModuleInterop": true, 23 | "module": "esnext", 24 | "moduleResolution": "node", 25 | "resolveJsonModule": true, 26 | "isolatedModules": true, 27 | "downlevelIteration": true, 28 | "incremental": true 29 | }, 30 | "exclude": [ 31 | "node_modules", 32 | "build", 33 | "scripts" 34 | ], 35 | "include": [ 36 | "next-env.d.ts", 37 | "**/*.ts", 38 | "**/*.tsx", 39 | "**/*.js", 40 | "**/.*.js" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /setup/overlayfs_genfstab: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """generate lines for /etc/fstab to automatically mount overlayfs roots 3 | Futher information: 4 | - fstab(5) 5 | - https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html#multiple-lower-layers 6 | - https://wiki.archlinux.org/index.php/Overlay_filesystem 7 | """ 8 | import json 9 | import os 10 | import sys 11 | 12 | image = sys.argv[1] 13 | 14 | with open("/var/cache/ATO/images/manifest.json") as f: 15 | layers = json.load(f)[0]["Layers"] 16 | 17 | 18 | def dedup(a): 19 | s = set() 20 | for i in a: 21 | if i in s: 22 | continue 23 | else: 24 | s.add(i) 25 | yield i 26 | 27 | 28 | layers = ":".join("/usr/local/lib/ATO/layers/" + layer.removesuffix(".tar") for layer in dedup(layers[::-1])) 29 | 30 | # Create mount point 31 | os.makedirs(f"/usr/local/lib/ATO/rootfs/{image}", exist_ok=True) 32 | 33 | # The final layer "/usr/local/share/ATO/overlayfs_upper" is needed to allow bwrap to mount a tmpfs etc. there. See 34 | # https://github.com/containers/bubblewrap/issues/413 for more info 35 | print(f"overlay /usr/local/lib/ATO/rootfs/{image} overlay lowerdir=/usr/local/share/ATO/overlayfs_upper:{layers} 0 0") 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dyalog.run 2 | Dyalog.run is an online sandbox for testing code using Dyalog APL. 3 | 4 | This project is forked from [Attempt This Online](https://ato.pxeger.com). 5 | 6 | Attempt This Online was originally a clone of [Try It Online!](https://github.com/TryItOnline/tryitonline) 7 | 8 | This repository contains the code to host dyalog.run, Please refer to [`LICENCE.txt`](./LICENCE.txt) if you wish 9 | to use this code in your own project. 10 | 11 | 12 | ## Licence 13 | 14 | ### Dyalog APL 15 | Dyalog APL is © Copyright Dyalog Ltd. It is proprietary software written by Dyalog Ltd and licensed under free-to-use "basic licence". For more information see the [Dyalog Licence Agreement](https://www.dyalog.com/uploads/documents/Developer_Software_Licence.pdf) and the [Terms and Conditions](https://www.dyalog.com/uploads/documents/Terms_and_Conditions.pdf). 16 | 17 | 18 | ### Dyalog.run 19 | © Patrick Reader, Dyalog Ltd. 20 | 21 | Dyalog.run is licensed under the GNU Affero General Public License 3.0 except where 22 | otherwise noted - see the [`LICENCE.txt`](./LICENCE.txt) file for more information. 23 | 24 | Note that if you use the instance at [dyalog.run](https://dyalog.run), you must also abide by the 25 | [Terms of Use](https://dyalog.run/legal#terms-of-use). 26 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "attempt_this_online", 3 | "version": "0.1.16", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build && next export", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "@heroicons/react": "^1.0.0", 12 | "@msgpack/msgpack": "^2.5.1", 13 | "@reduxjs/toolkit": "^1.5.1", 14 | "@types/lodash": "^4.14.168", 15 | "@types/node": "^17.0.14", 16 | "@types/pako": "^1.0.1", 17 | "js-base64": "^3.6.1", 18 | "localforage": "^1.9.0", 19 | "lodash": "^4.17.21", 20 | "next": "^12.0.10", 21 | "next-build-id": "^3.0.0", 22 | "pako": "^1.0.11", 23 | "prop-types": "^15.7.2", 24 | "react": "^17.0.2", 25 | "react-dom": "^17.0.2", 26 | "react-redux": "^7.2.6", 27 | "redux": "^4.1.2", 28 | "redux-devtools-extension": "^2.13.9" 29 | }, 30 | "devDependencies": { 31 | "@types/react": "^17.0.3", 32 | "@typescript-eslint/eslint-plugin": "^4.20.0", 33 | "@typescript-eslint/parser": "^4.20.0", 34 | "autoprefixer": "^10.4.2", 35 | "eslint": "^7.23.0", 36 | "eslint-config-airbnb-typescript": "^12.3.1", 37 | "postcss": "^8.4.6", 38 | "tailwindcss": "^3.0.18", 39 | "typescript": "^4.2.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Attempt This Online is a new project and there are bound to be security vulnerabilities. 3 | 4 | If you find a security vulnerability, email me at any email address ending in `@pxeger.com`. Do not report it as an 5 | issue in this repository. 6 | 7 | The setup script generates "flags", which are random text stored in locations that should only be readable by various 8 | users. You can use the contents of these flags to demostrate your vulnerability. The flags are stored in: 9 | - `/root/flag` (readable only by root) 10 | - `/var/local/lib/ATO_home/flag` (readable only by the `ato` user) 11 | 12 | This security policy does not give you permission to attempt to attack the official instance (ato.pxeger.com) or any of 13 | my servers. Test and demonstrate vulnerabilities only on your own personal instance. 14 | 15 | Note that the most effort will be put into supporting appliances using the canonical setup script; while I will do my 16 | best to make this project secure in all circumstances, vulnerabilities relating exclusively to different setups may not 17 | be prioritised. 18 | 19 | This software is provided ENTIRELY WITHOUT WARRANTY and the author(s) take NO RESPONSIBILITY for any damage caused as a 20 | result of its insecurity. See the [licence](./LICENCE.txt) for further details. 21 | -------------------------------------------------------------------------------- /frontend/lib/useSystemThemePreference.ts: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/neo4j/neo4j-browser/blob/6b1a9d96755383e0e7b1f209e1c10f73c3b198e5/src/browser/hooks/useDetectColorScheme.ts 2 | import { useState, useEffect } from 'react'; 3 | 4 | export default function useSystemThemePreference() { 5 | const [systemThemePreference, setSystemThemePreference] = useState<'dark' | 'light' | null>(null); 6 | 7 | useEffect(() => { 8 | if (!window.matchMedia) { 9 | // no browser support 10 | setSystemThemePreference(null); 11 | return; 12 | } 13 | 14 | const darkListener = (event: any) => event && event.matches && setSystemThemePreference('dark'); 15 | const darkQuery = window.matchMedia('(prefers-color-scheme: dark)'); 16 | darkQuery.addListener(darkListener); 17 | darkListener(darkQuery); 18 | const lightListener = (event: any) => event && event.matches && setSystemThemePreference('light'); 19 | const lightQuery = window.matchMedia('(prefers-color-scheme: light)'); 20 | lightQuery.addListener(lightListener); 21 | lightListener(lightQuery); 22 | 23 | // cleanup 24 | // eslint-disable-next-line consistent-return 25 | return () => { 26 | darkQuery.removeListener(darkListener); 27 | lightQuery.removeListener(lightListener); 28 | }; 29 | }, []); 30 | return systemThemePreference; 31 | } 32 | -------------------------------------------------------------------------------- /frontend/components/footer.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | // eslint-disable-next-line react/require-default-props 4 | export default function Footer({ noLegalLink = false }: { noLegalLink?: boolean }) { 5 | return ( 6 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | ## Fully Automated 3 | Download and execute the [setup script](./setup/setup) **on a fresh install of Arch Linux**. Using the setup script in 4 | any other distribution, or on an Arch Linux machine that has already had changes, is not supported. 5 | 6 | Attempt It Online is designed to be run as a wholly packaged appliance and as such requires a dedicated virtual machine. 7 | It cannot be run in Docker or similar. *If you do manage to get it working inside a container, please let me know as it 8 | would be very useful for me.* 9 | 10 | Upgrading is also not recommended beyond `pacman -Syu`, and there is no script for it. Instead, you should use an 11 | Infrastructure As Code tool like [Terraform](https://terraform.io) to automatically provision and set up new virtual 12 | machines with the new version (also using the setup script). 13 | 14 | ## Manually (not recommended) 15 | **Warning:** All the code and configuration files in this repository are tuned exactly to a fresh Arch Linux setup and 16 | you will need to change *a lot* of things to get it to work with a custom setup. Absolutely no changes will be made to 17 | support any other system setups. 18 | 19 | Read and follow the steps described in the source code of `setup/setup`, adjusting them to your setup as necessary. 20 | 21 | ## Uninstallation 22 | There is an uninstallation script, `setup/uninstall`, which stops all services and removes all configuration files. It 23 | does not remove any installed dependencies. 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 4 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= 8 | github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 11 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= 13 | github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= 14 | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= 15 | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 17 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 18 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 19 | -------------------------------------------------------------------------------- /frontend/components/resizeableText.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | 4 | export default function ResizeableText( 5 | { 6 | value, 7 | readOnly, 8 | id = undefined, 9 | onKeyDown = _ => undefined, 10 | dummy, 11 | setValue = _ => undefined, 12 | }: { 13 | value: string, 14 | readOnly: boolean, 15 | id?: string, 16 | onKeyDown?: (e: any) => void, 17 | dummy: any, 18 | setValue?: (e: string) => void, 19 | }, 20 | ) { 21 | const [height, setHeight] = useState(24); 22 | const bigTextBoxes = useSelector((state: any) => state.bigTextBoxes); 23 | const handleChange = (event: any) => { 24 | setValue(event.target.value); 25 | resize(event.target.value); 26 | }; 27 | const resize = (x = value) => { 28 | if (dummy.current) { 29 | dummy.current.value = x; 30 | setHeight(dummy.current.scrollHeight); 31 | dummy.current.value = ''; 32 | } 33 | }; 34 | useEffect(resize, [value]); 35 | const tabBehaviour = useSelector((state: any) => state.tabBehaviour); 36 | const handleKeyDown = (event: any) => { 37 | if (!readOnly && !event.metaKey && !event.altKey && !event.ctrlKey && tabBehaviour === 'insert' && event.key === 'Tab') { 38 | event.preventDefault(); 39 | const start = event.target.selectionStart; 40 | const end = event.target.selectionEnd; 41 | const newValue = value.slice(0, start) + '\t' + value.slice(end); 42 | event.target.value = newValue; 43 | event.target.selectionStart = start + 1; 44 | event.target.selectionEnd = start + 1; 45 | handleChange(event); 46 | } 47 | else { 48 | onKeyDown(event); 49 | } 50 | } 51 | return ( 52 | <> 53 |