├── LICENSE ├── README.md ├── dynamic_flag ├── .env ├── Dockerfile ├── cert.pem ├── docker-compose.yml ├── front.py └── xinetd ├── example ├── .env ├── Dockerfile ├── README.md ├── docker-compose.yml └── example.py └── web_netcat ├── .env ├── .gitignore ├── Dockerfile ├── app.ts ├── docker-compose.yml ├── package-lock.json ├── package.json └── static ├── index.html └── js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 USTC Hackergame 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hackergame nc 类题目的 Docker 容器资源限制、动态 flag、网页终端 2 | 3 | ## 快速入门 4 | 5 | ### 配置证书 6 | 7 | 证书用于验证用户 Token。请确保这里的证书文件(cert.pem)与 [Hackergame 平台](https://github.com/ustclug/hackergame) 配置的证书相同,这样 Hackergame 平台为每个用户生成的 Token 才可以通过这里的用户验证。 8 | 9 | 如果你仅仅想测试一下,可以使用 [dynamic_flag/cert.pem](dynamic_flag/cert.pem) 自带的证书,以及这个 Token: 10 | 11 | `1:MEUCIQCjK1QcPFro86w3bKPb5zUZZd96ocp3EZDFcwLtJxNNDAIgEPk3Orw0mE+zHLQA7e31kSFupNtG9uepz2H4EqxlKWY=` 12 | 13 | 在生产环境中,请使用自己生成的证书,方法如下: 14 | 15 | 生成私钥 `openssl ecparam -name secp256k1 -genkey -noout -out private.pem` 16 | 17 | 生成证书 `openssl req -x509 -key private.pem -out cert.pem -days 365` 18 | 19 | 然后将生成的 `cert.pem` 文件放在 [dynamic_flag/cert.pem](dynamic_flag/cert.pem)。 20 | 21 | ### 配置题目 22 | 23 | 如果你仅仅想测试一下示例题目,那么可以跳过此步骤。 24 | 25 | 本项目的目录结构设计为可以被 [Hackergame 平台的题目导入命令](https://github.com/ustclug/hackergame/blob/master/frontend/management/commands/import_data.py) 直接导入。 26 | 27 | [dynamic_flag](dynamic_flag) 目录中包含了题目容器化、连接限制、动态 flag 相关的逻辑。 28 | 29 | [web_netcat](web_netcat) 目录中包含了网页终端的逻辑。 30 | 31 | 如果仅仅是使用本项目,那么以上两个目录中的内容都不需要修改,它们也不会被 Hackergame 平台导入(因为没有 `README.md` 文件)。 32 | 33 | 示例题目在 [example](example) 目录中,其中的 [example/docker-compose.yml](example/docker-compose.yml) 中引用了以上两个目录中的内容。你可以把 example 目录复制多份为不同的名字,它们在被导入到 Hackergame 平台后会显示为多道题目。 34 | 35 | 题目是 Docker 化的,注意每次运行题目 Docker 时 **只启动一个题目的实例,通过标准输入输出交互,你的题目不需要监听端口,也不需要做任何资源限制。** 参见 [example/Dockerfile](example/Dockerfile) 和 [example/example.py](example/example.py)。 36 | 37 | 你需要修改 [example/.env](example/.env) 文件,针对题目的情况进行配置,包括 nc 的端口(`port`)、网页终端的端口(`web_port`)、运行时间和资源限制、flag 文件位置、动态 flag 规则、题目的容器名称等。动态 flag 的生成方法可以由你自己决定,可以使用类似 `'flag{prefix_' + sha256('secret'+token)[:10] + '}'` 的方案,示例中使用了 Python 的 f-string。对于多个 flag 的情况,`flag_path` 中路径和 `flag_rule` 中 Python 表达式都用 `,` 分隔即可。容器名称(`challenge_docker_name`)是 docker-compose 自动命名的,请设置为目录名 + "_challenge"。对于每一个连接,如果 Token 合法并且连接频率没有超过限制,那么你的题目容器会以指定的资源限制启动,动态生成的 flag 会被挂载到指定的路径,选手的 TCP 连接将会被连接到容器的标准输入输出上。如果你的题目需要获得用户 Token,直接读取 `hackergame_token` 环境变量即可。 38 | 39 | [example/README.md](example/README.md) 是用于导入 Hackergame 平台的,里面配置的 flag 需要与 `.env` 中配置的 flag 相同,端口也需要进行相应修改。 40 | 41 | ### 运行题目 42 | 43 | 在 [example](example) 目录中运行 `docker-compose up --build` 即可,然后你可以通过 `nc 127.0.0.1 10000` 来连接,也可以使用 [http://127.0.0.1:10001/](http://127.0.0.1:10001/) 的网页终端。 44 | 45 | ## 本项目的背景 46 | 47 | 与很多 CTF 比赛类似,USTC Hackergame 需要运行选手与服务器交互的 nc 类题目。然而 CTF 比赛中常见的做法有以下问题: 48 | 49 | - 通过求解 PoW 来做题目的连接限制,对新手不友好,也在某种程度上影响比赛的体验 50 | 51 | - pwn 题缺少真正有效的资源限制。我调研了很多开源的 Docker 化方案,也咨询了很多比赛的出题人,结论是现有的方案都无法真正防止针对性的资源耗尽攻击(所谓“搅屎”)。很多 pwn 题的部署方案会限制选手能够使用的命令,这只是增加了资源耗尽攻击的难度而已,并没有从根本上解决问题。 52 | 53 | - 动态 flag、监听端口很多时候是题目逻辑的一部分,而我想做到这部分逻辑对出题人是透明的,这样也可以让题目更统一、更稳定。 54 | 55 | 因为以上提到的原因,我在 Hackergame 2019 前开发了这套系统,并且在 Hackergame 2020 前进行了一些改进,但是这部分代码一直没有开源。 56 | 57 | 如今,我把这份代码以 MIT 协议开源出来,欢迎大家测试、使用和改进。 58 | 59 | ## 本项目的功能 60 | 61 | - 对用户 Token 进行验证,只有合法的 Token 才可以运行题目 62 | 63 | - 根据 Token 中的用户 ID 进行连接频率限制 64 | 65 | - 根据 Token 动态生成 flag,并自动挂载进题目 Docker 66 | 67 | - 限制题目的资源使用,包括限时、进程数限制、内存限制、不允许联网等 68 | 69 | - 保证题目的稳定性和安全性,用户无论在题目 docker 中做什么,都不会影响其他用户做题 70 | 71 | - 为题目提供一个网页终端,方便新手直接在网页上尝试做题,配合 Hackergame 平台可以实现 Token 的自动填充。 72 | 73 | ## 本项目的限制 74 | 75 | 本项目只适用于每个连接启动一个进程的题目,包括 pwn 题和其他的 nc 连接服务器类题目。 76 | 77 | 如果你的题目是一个一直运行的应用,例如 flask app,那么请自己进行 Token 的验证和动态 flag 生成。 78 | 79 | Token 的验证方法见 [dynamic_flag/front.py](dynamic_flag/front.py) 中的 `validate` 函数。由于 Token 是非对称签名,所以证书和验证代码完全可以公开。 80 | 81 | 如果不需要进行连接限制,那么不验证 Token 的合法性也无妨。 82 | 83 | 自己实现对用户的连接限制时,注意请按用户 id 限制,不要按 Token 限制,因为签名系统不保证每个 id 只有唯一的合法签名。 84 | 85 | ## 已知问题 86 | 87 | - 证书是否过期不会被检查 88 | 89 | - Windows 系统上可能无法正常使用,Linux 和 macOS 经测试没有问题 90 | -------------------------------------------------------------------------------- /dynamic_flag/.env: -------------------------------------------------------------------------------- 1 | port=2333 2 | conn_interval=10 3 | token_timeout=30 4 | challenge_timeout=30 5 | pids_limit=16 6 | mem_limit=128m 7 | flag_path=/home/ctf/flag 8 | flag_rule=f"flag{{welcome_{sha256('secret'+token)[:10]}}}" 9 | challenge_docker_name=test_challenge_ctf 10 | read_only=1 11 | flag_suid= 12 | challenge_network= 13 | shm_exec=0 14 | tmp_tmpfs=0 15 | extra_flag= 16 | -------------------------------------------------------------------------------- /dynamic_flag/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12 2 | RUN apt update && apt -y upgrade && \ 3 | apt install --no-install-recommends -y xinetd python3-openssl docker.io && \ 4 | rm -rf /var/lib/apt/lists/* 5 | COPY xinetd /etc/xinetd.d/ctf 6 | COPY front.py / 7 | COPY cert.pem / 8 | CMD ["xinetd", "-dontfork"] 9 | -------------------------------------------------------------------------------- /dynamic_flag/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIByjCCAXCgAwIBAgIUTVIbMFuhApks+IlLfPLsIV0rw9cwCgYIKoZIzj0EAwIw 3 | PDELMAkGA1UEBhMCQVUxCjAIBgNVBAgMAScxITAfBgNVBAoMGEludGVybmV0IFdp 4 | ZGdpdHMgUHR5IEx0ZDAeFw0yMTA0MTgxNjU4MzRaFw0yMjA0MTgxNjU4MzRaMDwx 5 | CzAJBgNVBAYTAkFVMQowCAYDVQQIDAEnMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn 6 | aXRzIFB0eSBMdGQwVjAQBgcqhkjOPQIBBgUrgQQACgNCAAREnpLsdtmenQf0Iw2Z 7 | 5xLOgDYa9VpLU3C1Gxm9TpJi4eAaX8kPpYVkD1rsjE9SOt6/GLnYRTytrlJOGQ/X 8 | nL5Ao1MwUTAdBgNVHQ4EFgQU3BPqL8FbENPzF1rj00aMFzyXXjAwHwYDVR0jBBgw 9 | FoAU3BPqL8FbENPzF1rj00aMFzyXXjAwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjO 10 | PQQDAgNIADBFAiAT/QceAhSZRkiqLh6Udhey2etTr7L08b+G6k2r8HSfswIhAM7Y 11 | TEh3QVp8F5UvzO5g/OtTb0/gS41kvY8OU8AbMI8T 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /dynamic_flag/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | services: 3 | front: 4 | build: . 5 | ports: 6 | - ${port}:2333 7 | restart: always 8 | read_only: true 9 | ipc: shareable 10 | volumes: 11 | - /var/run/docker.sock:/var/run/docker.sock 12 | ulimits: 13 | nofile: 14 | soft: 65536 15 | hard: 65536 16 | environment: 17 | - hackergame_conn_interval=${conn_interval} 18 | - hackergame_token_timeout=${token_timeout} 19 | - hackergame_challenge_timeout=${challenge_timeout} 20 | - hackergame_pids_limit=${pids_limit} 21 | - hackergame_mem_limit=${mem_limit} 22 | - hackergame_flag_path=${flag_path} 23 | - hackergame_flag_rule=${flag_rule} 24 | - hackergame_challenge_docker_name=${challenge_docker_name} 25 | - hackergame_read_only=${read_only} 26 | - hackergame_flag_suid=${flag_suid} 27 | - hackergame_challenge_network=${challenge_network} 28 | - hackergame_shm_exec=${shm_exec} 29 | - hackergame_tmp_tmpfs=${tmp_tmpfs} 30 | - hackergame_extra_flag=${extra_flag} 31 | - TZ=Asia/Shanghai 32 | - DOCKER_DEFAULT_PLATFORM=linux/amd64 33 | -------------------------------------------------------------------------------- /dynamic_flag/front.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import OpenSSL 3 | import os 4 | import time 5 | import fcntl 6 | import signal 7 | import tempfile 8 | import hashlib 9 | import atexit 10 | import subprocess 11 | from datetime import datetime 12 | import threading 13 | import select 14 | import sys 15 | 16 | tmp_path = "/dev/shm/hackergame" 17 | tmp_flag_path = "/dev/shm" 18 | conn_interval = int(os.environ["hackergame_conn_interval"]) 19 | token_timeout = int(os.environ["hackergame_token_timeout"]) 20 | challenge_timeout = int(os.environ["hackergame_challenge_timeout"]) 21 | pids_limit = int(os.environ["hackergame_pids_limit"]) 22 | mem_limit = os.environ["hackergame_mem_limit"] 23 | flag_path = os.environ["hackergame_flag_path"] 24 | flag_rule = os.environ["hackergame_flag_rule"] 25 | challenge_docker_name = os.environ["hackergame_challenge_docker_name"] 26 | read_only = 0 if os.environ.get("hackergame_read_only") == "0" else 1 27 | 28 | # flag_suid sets whether set stricter permission requirements (0400 instead of 0444) to corresponding flag file 29 | flag_suid = os.environ.get("hackergame_flag_suid", "").split(",") 30 | # challenge_network sets whether the challenge container can access other networks. Default = no access 31 | challenge_network = os.environ.get("hackergame_challenge_network", "") 32 | # shm_exec sets /dev/shm no longer be noexec. Default = keep noexec 33 | shm_exec = 1 if os.environ.get("hackergame_shm_exec") == "1" else 0 34 | # tmp_tmpfs sets whether to explicitly mount /tmp as tmpfs. Default = no 35 | tmp_tmpfs = 1 if os.environ.get("hackergame_tmp_tmpfs") == "1" else 0 36 | # extra_flag directly appends to "docker create ..." 37 | extra_flag = os.environ.get("hackergame_extra_flag", "") 38 | 39 | with open("cert.pem") as f: 40 | cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, f.read()) 41 | 42 | 43 | class Flag: 44 | def __init__(self, flag, suid): 45 | self.flag = flag 46 | self.suid = suid 47 | 48 | 49 | def validate(token): 50 | try: 51 | id, sig = token.split(":", 1) 52 | sig = base64.b64decode(sig, validate=True) 53 | OpenSSL.crypto.verify(cert, sig, id.encode(), "sha256") 54 | return id 55 | except Exception: 56 | return None 57 | 58 | 59 | def try_login(id): 60 | os.makedirs(tmp_path, mode=0o700, exist_ok=True) 61 | fd = os.open(os.path.join(tmp_path, id), os.O_CREAT | os.O_RDWR) 62 | fcntl.flock(fd, fcntl.LOCK_EX) 63 | with os.fdopen(fd, "r+") as f: 64 | data = f.read() 65 | now = int(time.time()) 66 | if data: 67 | last_login, balance = data.split() 68 | last_login = int(last_login) 69 | balance = int(balance) 70 | last_login_str = ( 71 | datetime.fromtimestamp(last_login).isoformat().replace("T", " ") 72 | ) 73 | balance += now - last_login 74 | if balance > conn_interval * 3: 75 | balance = conn_interval * 3 76 | else: 77 | balance = conn_interval * 3 78 | if conn_interval > balance: 79 | print( 80 | f"Player connection rate limit exceeded, please try again after {conn_interval-balance} seconds. " 81 | f"连接过于频繁,超出服务器限制,请等待 {conn_interval-balance} 秒后重试。" 82 | ) 83 | return False 84 | balance -= conn_interval 85 | f.seek(0) 86 | f.truncate() 87 | f.write(str(now) + " " + str(balance)) 88 | return True 89 | 90 | 91 | def check_token(): 92 | signal.alarm(token_timeout) 93 | print("Please input your token: ") 94 | with os.fdopen(sys.stdin.fileno(), 'rb', buffering=0, closefd=False) as unbuffered_stdin: 95 | token = unbuffered_stdin.readline().decode().strip() 96 | id = validate(token) 97 | if not id: 98 | print("Invalid token") 99 | exit(-1) 100 | if not try_login(id): 101 | exit(-1) 102 | signal.alarm(0) 103 | return token, id 104 | 105 | 106 | def generate_flags(token): 107 | functions = {} 108 | for method in "md5", "sha1", "sha256": 109 | 110 | def f(s, method=method): 111 | return getattr(hashlib, method)(s.encode()).hexdigest() 112 | 113 | functions[method] = f 114 | 115 | if flag_path: 116 | flag = eval(flag_rule, functions, {"token": token}) 117 | if isinstance(flag, tuple): 118 | res = dict(zip(flag_path.split(","), flag)) 119 | else: 120 | res = {flag_path: flag} 121 | for path in res: 122 | if path in flag_suid: 123 | res[path] = Flag(flag=res[path], suid=True) 124 | else: 125 | res[path] = Flag(flag=res[path], suid=False) 126 | return res 127 | else: 128 | return {} 129 | 130 | 131 | def generate_flag_files(flags): 132 | flag_files = {} 133 | for flag_path, flag in flags.items(): 134 | with tempfile.NamedTemporaryFile("w", delete=False, dir=tmp_flag_path) as f: 135 | f.write(flag.flag + "\n") 136 | fn = f.name 137 | if flag.suid: 138 | os.chmod(fn, 0o400) 139 | else: 140 | os.chmod(fn, 0o444) 141 | flag_files[flag_path] = fn 142 | return flag_files 143 | 144 | 145 | def cleanup(): 146 | if child_docker_id: 147 | subprocess.run( 148 | f"docker rm -f {child_docker_id}", 149 | shell=True, 150 | stdout=subprocess.DEVNULL, 151 | stderr=subprocess.DEVNULL, 152 | ) 153 | for file in flag_files.values(): 154 | try: 155 | os.unlink(file) 156 | except FileNotFoundError: 157 | pass 158 | 159 | 160 | def check_docker_image_exists(docker_image_name): 161 | return subprocess.run( 162 | f"docker inspect --type=image {docker_image_name}", 163 | shell=True, 164 | stdout=subprocess.DEVNULL, 165 | stderr=subprocess.DEVNULL, 166 | ).returncode == 0 167 | 168 | 169 | def create_docker(flag_files, id): 170 | network = "none" 171 | if challenge_network: 172 | network = challenge_network.split()[0] 173 | cmd = ( 174 | f"docker create --init --rm -i --network {network} " 175 | f"--pids-limit {pids_limit} -m {mem_limit} --memory-swap {mem_limit} --cpus 1 " 176 | f"-e hackergame_token=$hackergame_token " 177 | ) 178 | 179 | if read_only: 180 | cmd += "--read-only " 181 | if shm_exec: 182 | cmd += "--tmpfs /dev/shm:exec " 183 | if tmp_tmpfs: 184 | cmd += "--tmpfs /tmp " 185 | if extra_flag: 186 | cmd += extra_flag + " " 187 | 188 | # new version docker-compose uses "-" instead of "_" in the image name, so we try both 189 | challenge_docker_name_checked = challenge_docker_name 190 | if challenge_docker_name.endswith("_challenge"): 191 | name_prefix = challenge_docker_name[:-10] 192 | if not check_docker_image_exists(challenge_docker_name): 193 | challenge_docker_name_checked = name_prefix + "-challenge" 194 | elif challenge_docker_name.endswith("-challenge"): 195 | name_prefix = challenge_docker_name[:-10] 196 | if not check_docker_image_exists(challenge_docker_name): 197 | challenge_docker_name_checked = name_prefix + "_challenge" 198 | else: 199 | name_prefix = challenge_docker_name 200 | 201 | if not check_docker_image_exists(challenge_docker_name_checked): 202 | print("Docker image does not exist, please contact admin") 203 | exit(-1) 204 | 205 | timestr = datetime.now().strftime("%m%d_%H%M%S_%f")[:-3] 206 | child_docker_name = f"{name_prefix}_u{id}_{timestr}" 207 | cmd += f'--name "{child_docker_name}" ' 208 | 209 | with open("/etc/hostname") as f: 210 | hostname = f.read().strip() 211 | with open("/proc/self/mountinfo") as f: 212 | for part in f.read().split('/'): 213 | if len(part) == 64 and part.startswith(hostname): 214 | docker_id = part 215 | break 216 | else: 217 | raise ValueError('Docker ID not found') 218 | prefix = f"/var/lib/docker/containers/{docker_id}/mounts/shm/" 219 | 220 | for flag_path, fn in flag_files.items(): 221 | flag_src_path = prefix + fn.split("/")[-1] 222 | cmd += f"-v {flag_src_path}:{flag_path}:ro " 223 | 224 | cmd += challenge_docker_name_checked 225 | 226 | return subprocess.check_output(cmd, shell=True).decode().strip() 227 | 228 | 229 | def print_exitcode(code: int): 230 | print() 231 | if code >= 0: 232 | print(f"(Environment exited with return code {code})", file=sys.stderr) 233 | else: 234 | signal_number = -code 235 | try: 236 | signal_name = signal.Signals(signal_number).name 237 | print(f"(Environment exited with signal {signal_name})", file=sys.stderr) 238 | except ValueError: 239 | print(f"(Environment exited with unknown signal number {signal_number})", file=sys.stderr) 240 | 241 | 242 | def run_docker(child_docker_id): 243 | # timeout command sends SIGKILL to docker-cli, and the container would be stopped 244 | # in cleanup(). Please note that this command SHALL NOT BE RUN WITH Debian's dash! 245 | # Otherwise, when client (player) & server's buffers are all full, dash would be 246 | # BLOCKED when writing "Killed", and this would hang for a very long time! 247 | 248 | p = subprocess.run([ 249 | "timeout", "-s", "9", str(challenge_timeout), "docker", "start", "-i", child_docker_id 250 | ]) 251 | # As is mentioned above, outputting status SHALL NEVER block main thread... 252 | t = threading.Thread(target=print_exitcode, args=(p.returncode,), daemon=True) 253 | t.start() 254 | 255 | # If users' network buffer is blocked, that not our fault... 256 | # wait 1s, and just leave. 257 | time.sleep(1) 258 | 259 | 260 | def clean_on_socket_close(): 261 | p = select.poll() 262 | p.register(sys.stdin, select.POLLHUP | select.POLLERR | select.POLLRDHUP) 263 | p.poll() 264 | 265 | # If the user closes the socket before `docker create`, it will cause `cleanup()` 266 | # to prematurely delete the flag, resulting in a race condition, which causes the 267 | # flag inside the challenge container to turn into a directory. Here, we ensure 268 | # that `cleanup()` only occurs after `docker create` has completed. 269 | while child_docker_id is None: 270 | time.sleep(0.1) 271 | time.sleep(1) 272 | 273 | cleanup() 274 | 275 | 276 | if __name__ == "__main__": 277 | child_docker_id = None 278 | flag_files = {} 279 | atexit.register(cleanup) 280 | t = threading.Thread(target=clean_on_socket_close, daemon=True) 281 | t.start() 282 | 283 | token, id = check_token() 284 | os.environ["hackergame_token"] = token 285 | flags = generate_flags(token) 286 | flag_files = generate_flag_files(flags) 287 | child_docker_id = create_docker(flag_files, id) 288 | run_docker(child_docker_id) 289 | -------------------------------------------------------------------------------- /dynamic_flag/xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | server = /usr/bin/python3 4 | server_args = -u /front.py 5 | port = 2333 6 | protocol = tcp 7 | type = UNLISTED 8 | user = root 9 | wait = no 10 | flags = NODELAY 11 | } 12 | -------------------------------------------------------------------------------- /example/.env: -------------------------------------------------------------------------------- 1 | port=10000 2 | web_port=10001 3 | nc_host=front 4 | nc_port=2333 5 | nc_raw=0 6 | conn_interval=10 7 | token_timeout=30 8 | challenge_timeout=300 9 | pids_limit=16 10 | mem_limit=256m 11 | read_only=1 12 | flag_path=/flag1,/flag2 13 | flag_rule=f"flag{{this_is_an_example_{sha256('example1'+token)[:10]}}}",f"flag{{this_is_the_second_flag_{sha256('example2'+token)[:10]}}}" 14 | challenge_docker_name=example_challenge 15 | flag_suid= 16 | challenge_network= 17 | shm_exec=0 18 | -------------------------------------------------------------------------------- /example/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | COPY example.py / 3 | CMD ["/usr/local/bin/python3", "-u", "/example.py"] 4 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | enabled: true 3 | name: 示例题目 4 | category: general 5 | url: http://127.0.0.1:10001/?token={token} 6 | prompt: flag{...} 7 | index: 0 8 | flags: 9 | - name: flag1 10 | score: 100 11 | type: expr 12 | flag: f"flag{{this_is_an_example_{sha256('example1'+token)[:10]}}}" 13 | - name: flag2 14 | score: 200 15 | type: expr 16 | flag: f"flag{{this_is_the_second_flag_{sha256('example2'+token)[:10]}}}" 17 | --- 18 | 19 | 你可以通过 `nc 127.0.0.1 10000` 来连接题目,或者点击下面的「打开/下载题目」按钮通过网页终端与远程交互。 20 | 21 | > 如果你不知道 `nc` 是什么,或者在使用上面的命令时遇到了困难,可以参考我们编写的 [萌新入门手册:如何使用 nc/ncat?](https://lug.ustc.edu.cn/planet/2019/09/how-to-use-nc/) 22 | -------------------------------------------------------------------------------- /example/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | services: 3 | challenge: 4 | build: . 5 | entrypoint: ["/bin/true"] 6 | front: 7 | extends: 8 | file: ../dynamic_flag/docker-compose.yml 9 | service: front 10 | depends_on: 11 | - challenge 12 | web: 13 | extends: 14 | file: ../web_netcat/docker-compose.yml 15 | service: web 16 | -------------------------------------------------------------------------------- /example/example.py: -------------------------------------------------------------------------------- 1 | print("Your first flag:", open("flag1").read()) 2 | print("Answer the question to get your second flag") 3 | if input("1+1=").strip() == "2": 4 | print(open("flag2").read()) 5 | else: 6 | print("Wrong!") 7 | -------------------------------------------------------------------------------- /web_netcat/.env: -------------------------------------------------------------------------------- 1 | nc_host=bbs.ustc.edu.cn 2 | nc_port=23 3 | web_port=3000 4 | -------------------------------------------------------------------------------- /web_netcat/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | app.js 3 | -------------------------------------------------------------------------------- /web_netcat/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 2 | RUN apt update && apt -y upgrade && \ 3 | apt install --no-install-recommends -y netcat-traditional && \ 4 | rm -rf /var/lib/apt/lists/* 5 | WORKDIR /srv 6 | COPY package-lock.json package.json ./ 7 | RUN npm i 8 | COPY static ./static 9 | COPY app.ts ./ 10 | RUN ./node_modules/.bin/tsc app.ts 11 | CMD ["node", "app.js"] 12 | -------------------------------------------------------------------------------- /web_netcat/app.ts: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const app = express(); 3 | const expressWs = require('express-ws')(app); 4 | const pty = require('node-pty'); 5 | 6 | app.use(express.static('static')); 7 | 8 | app.ws('/shell', (ws, req) => { 9 | var shell; 10 | if (Number(process.env.nc_raw)) { 11 | shell = pty.spawn('/bin/bash', ['-c', 'stty raw -echo; nc ' + process.env.nc_host + ' ' + process.env.nc_port]); 12 | } else { 13 | shell = pty.spawn('/bin/nc', [process.env.nc_host, process.env.nc_port]); 14 | } 15 | shell.on('data', (data) => { 16 | ws.send(data); 17 | }); 18 | ws.on('message', (msg) => { 19 | shell.write(msg); 20 | }); 21 | shell.on('close', () => { 22 | ws.close(); 23 | }); 24 | ws.on('close', () => { 25 | shell.kill(); 26 | }); 27 | }); 28 | 29 | app.listen(3000); 30 | -------------------------------------------------------------------------------- /web_netcat/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | services: 3 | web: 4 | build: . 5 | ports: 6 | - ${web_port}:3000 7 | environment: 8 | - nc_host=${nc_host} 9 | - nc_port=${nc_port} 10 | - nc_raw=${nc_raw} 11 | restart: always 12 | init: true 13 | -------------------------------------------------------------------------------- /web_netcat/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web_netcat", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@types/node": "^18.11.8", 9 | "@xterm/addon-attach": "^0.11.0", 10 | "@xterm/addon-fit": "^0.10.0", 11 | "@xterm/xterm": "^5.5.0", 12 | "express": "^4.21.0", 13 | "express-ws": "^5.0.2", 14 | "node-pty": "^1.0.0", 15 | "typescript": "^5.6.2" 16 | } 17 | }, 18 | "node_modules/@types/node": { 19 | "version": "18.19.54", 20 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", 21 | "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", 22 | "license": "MIT", 23 | "dependencies": { 24 | "undici-types": "~5.26.4" 25 | } 26 | }, 27 | "node_modules/@xterm/addon-attach": { 28 | "version": "0.11.0", 29 | "resolved": "https://registry.npmjs.org/@xterm/addon-attach/-/addon-attach-0.11.0.tgz", 30 | "integrity": "sha512-JboCN0QAY6ZLY/SSB/Zl2cQ5zW1Eh4X3fH7BnuR1NB7xGRhzbqU2Npmpiw/3zFlxDaU88vtKzok44JKi2L2V2Q==", 31 | "license": "MIT", 32 | "peerDependencies": { 33 | "@xterm/xterm": "^5.0.0" 34 | } 35 | }, 36 | "node_modules/@xterm/addon-fit": { 37 | "version": "0.10.0", 38 | "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", 39 | "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==", 40 | "license": "MIT", 41 | "peerDependencies": { 42 | "@xterm/xterm": "^5.0.0" 43 | } 44 | }, 45 | "node_modules/@xterm/xterm": { 46 | "version": "5.5.0", 47 | "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", 48 | "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", 49 | "license": "MIT" 50 | }, 51 | "node_modules/accepts": { 52 | "version": "1.3.8", 53 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 54 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 55 | "license": "MIT", 56 | "dependencies": { 57 | "mime-types": "~2.1.34", 58 | "negotiator": "0.6.3" 59 | }, 60 | "engines": { 61 | "node": ">= 0.6" 62 | } 63 | }, 64 | "node_modules/array-flatten": { 65 | "version": "1.1.1", 66 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 67 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", 68 | "license": "MIT" 69 | }, 70 | "node_modules/body-parser": { 71 | "version": "1.20.3", 72 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", 73 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", 74 | "license": "MIT", 75 | "dependencies": { 76 | "bytes": "3.1.2", 77 | "content-type": "~1.0.5", 78 | "debug": "2.6.9", 79 | "depd": "2.0.0", 80 | "destroy": "1.2.0", 81 | "http-errors": "2.0.0", 82 | "iconv-lite": "0.4.24", 83 | "on-finished": "2.4.1", 84 | "qs": "6.13.0", 85 | "raw-body": "2.5.2", 86 | "type-is": "~1.6.18", 87 | "unpipe": "1.0.0" 88 | }, 89 | "engines": { 90 | "node": ">= 0.8", 91 | "npm": "1.2.8000 || >= 1.4.16" 92 | } 93 | }, 94 | "node_modules/bytes": { 95 | "version": "3.1.2", 96 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 97 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 98 | "license": "MIT", 99 | "engines": { 100 | "node": ">= 0.8" 101 | } 102 | }, 103 | "node_modules/call-bind": { 104 | "version": "1.0.7", 105 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 106 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 107 | "license": "MIT", 108 | "dependencies": { 109 | "es-define-property": "^1.0.0", 110 | "es-errors": "^1.3.0", 111 | "function-bind": "^1.1.2", 112 | "get-intrinsic": "^1.2.4", 113 | "set-function-length": "^1.2.1" 114 | }, 115 | "engines": { 116 | "node": ">= 0.4" 117 | }, 118 | "funding": { 119 | "url": "https://github.com/sponsors/ljharb" 120 | } 121 | }, 122 | "node_modules/content-disposition": { 123 | "version": "0.5.4", 124 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 125 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 126 | "license": "MIT", 127 | "dependencies": { 128 | "safe-buffer": "5.2.1" 129 | }, 130 | "engines": { 131 | "node": ">= 0.6" 132 | } 133 | }, 134 | "node_modules/content-type": { 135 | "version": "1.0.5", 136 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 137 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 138 | "license": "MIT", 139 | "engines": { 140 | "node": ">= 0.6" 141 | } 142 | }, 143 | "node_modules/cookie": { 144 | "version": "0.6.0", 145 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 146 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", 147 | "license": "MIT", 148 | "engines": { 149 | "node": ">= 0.6" 150 | } 151 | }, 152 | "node_modules/cookie-signature": { 153 | "version": "1.0.6", 154 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 155 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", 156 | "license": "MIT" 157 | }, 158 | "node_modules/debug": { 159 | "version": "2.6.9", 160 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 161 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 162 | "license": "MIT", 163 | "dependencies": { 164 | "ms": "2.0.0" 165 | } 166 | }, 167 | "node_modules/define-data-property": { 168 | "version": "1.1.4", 169 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 170 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 171 | "license": "MIT", 172 | "dependencies": { 173 | "es-define-property": "^1.0.0", 174 | "es-errors": "^1.3.0", 175 | "gopd": "^1.0.1" 176 | }, 177 | "engines": { 178 | "node": ">= 0.4" 179 | }, 180 | "funding": { 181 | "url": "https://github.com/sponsors/ljharb" 182 | } 183 | }, 184 | "node_modules/depd": { 185 | "version": "2.0.0", 186 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 187 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 188 | "license": "MIT", 189 | "engines": { 190 | "node": ">= 0.8" 191 | } 192 | }, 193 | "node_modules/destroy": { 194 | "version": "1.2.0", 195 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 196 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 197 | "license": "MIT", 198 | "engines": { 199 | "node": ">= 0.8", 200 | "npm": "1.2.8000 || >= 1.4.16" 201 | } 202 | }, 203 | "node_modules/ee-first": { 204 | "version": "1.1.1", 205 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 206 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 207 | "license": "MIT" 208 | }, 209 | "node_modules/encodeurl": { 210 | "version": "2.0.0", 211 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 212 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 213 | "license": "MIT", 214 | "engines": { 215 | "node": ">= 0.8" 216 | } 217 | }, 218 | "node_modules/es-define-property": { 219 | "version": "1.0.0", 220 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 221 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 222 | "license": "MIT", 223 | "dependencies": { 224 | "get-intrinsic": "^1.2.4" 225 | }, 226 | "engines": { 227 | "node": ">= 0.4" 228 | } 229 | }, 230 | "node_modules/es-errors": { 231 | "version": "1.3.0", 232 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 233 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 234 | "license": "MIT", 235 | "engines": { 236 | "node": ">= 0.4" 237 | } 238 | }, 239 | "node_modules/escape-html": { 240 | "version": "1.0.3", 241 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 242 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", 243 | "license": "MIT" 244 | }, 245 | "node_modules/etag": { 246 | "version": "1.8.1", 247 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 248 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 249 | "license": "MIT", 250 | "engines": { 251 | "node": ">= 0.6" 252 | } 253 | }, 254 | "node_modules/express": { 255 | "version": "4.21.0", 256 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", 257 | "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", 258 | "license": "MIT", 259 | "dependencies": { 260 | "accepts": "~1.3.8", 261 | "array-flatten": "1.1.1", 262 | "body-parser": "1.20.3", 263 | "content-disposition": "0.5.4", 264 | "content-type": "~1.0.4", 265 | "cookie": "0.6.0", 266 | "cookie-signature": "1.0.6", 267 | "debug": "2.6.9", 268 | "depd": "2.0.0", 269 | "encodeurl": "~2.0.0", 270 | "escape-html": "~1.0.3", 271 | "etag": "~1.8.1", 272 | "finalhandler": "1.3.1", 273 | "fresh": "0.5.2", 274 | "http-errors": "2.0.0", 275 | "merge-descriptors": "1.0.3", 276 | "methods": "~1.1.2", 277 | "on-finished": "2.4.1", 278 | "parseurl": "~1.3.3", 279 | "path-to-regexp": "0.1.10", 280 | "proxy-addr": "~2.0.7", 281 | "qs": "6.13.0", 282 | "range-parser": "~1.2.1", 283 | "safe-buffer": "5.2.1", 284 | "send": "0.19.0", 285 | "serve-static": "1.16.2", 286 | "setprototypeof": "1.2.0", 287 | "statuses": "2.0.1", 288 | "type-is": "~1.6.18", 289 | "utils-merge": "1.0.1", 290 | "vary": "~1.1.2" 291 | }, 292 | "engines": { 293 | "node": ">= 0.10.0" 294 | } 295 | }, 296 | "node_modules/express-ws": { 297 | "version": "5.0.2", 298 | "resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz", 299 | "integrity": "sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==", 300 | "license": "BSD-2-Clause", 301 | "dependencies": { 302 | "ws": "^7.4.6" 303 | }, 304 | "engines": { 305 | "node": ">=4.5.0" 306 | }, 307 | "peerDependencies": { 308 | "express": "^4.0.0 || ^5.0.0-alpha.1" 309 | } 310 | }, 311 | "node_modules/finalhandler": { 312 | "version": "1.3.1", 313 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", 314 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", 315 | "license": "MIT", 316 | "dependencies": { 317 | "debug": "2.6.9", 318 | "encodeurl": "~2.0.0", 319 | "escape-html": "~1.0.3", 320 | "on-finished": "2.4.1", 321 | "parseurl": "~1.3.3", 322 | "statuses": "2.0.1", 323 | "unpipe": "~1.0.0" 324 | }, 325 | "engines": { 326 | "node": ">= 0.8" 327 | } 328 | }, 329 | "node_modules/forwarded": { 330 | "version": "0.2.0", 331 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 332 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 333 | "license": "MIT", 334 | "engines": { 335 | "node": ">= 0.6" 336 | } 337 | }, 338 | "node_modules/fresh": { 339 | "version": "0.5.2", 340 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 341 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 342 | "license": "MIT", 343 | "engines": { 344 | "node": ">= 0.6" 345 | } 346 | }, 347 | "node_modules/function-bind": { 348 | "version": "1.1.2", 349 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 350 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 351 | "license": "MIT", 352 | "funding": { 353 | "url": "https://github.com/sponsors/ljharb" 354 | } 355 | }, 356 | "node_modules/get-intrinsic": { 357 | "version": "1.2.4", 358 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 359 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 360 | "license": "MIT", 361 | "dependencies": { 362 | "es-errors": "^1.3.0", 363 | "function-bind": "^1.1.2", 364 | "has-proto": "^1.0.1", 365 | "has-symbols": "^1.0.3", 366 | "hasown": "^2.0.0" 367 | }, 368 | "engines": { 369 | "node": ">= 0.4" 370 | }, 371 | "funding": { 372 | "url": "https://github.com/sponsors/ljharb" 373 | } 374 | }, 375 | "node_modules/gopd": { 376 | "version": "1.0.1", 377 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 378 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 379 | "license": "MIT", 380 | "dependencies": { 381 | "get-intrinsic": "^1.1.3" 382 | }, 383 | "funding": { 384 | "url": "https://github.com/sponsors/ljharb" 385 | } 386 | }, 387 | "node_modules/has-property-descriptors": { 388 | "version": "1.0.2", 389 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 390 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 391 | "license": "MIT", 392 | "dependencies": { 393 | "es-define-property": "^1.0.0" 394 | }, 395 | "funding": { 396 | "url": "https://github.com/sponsors/ljharb" 397 | } 398 | }, 399 | "node_modules/has-proto": { 400 | "version": "1.0.3", 401 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 402 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 403 | "license": "MIT", 404 | "engines": { 405 | "node": ">= 0.4" 406 | }, 407 | "funding": { 408 | "url": "https://github.com/sponsors/ljharb" 409 | } 410 | }, 411 | "node_modules/has-symbols": { 412 | "version": "1.0.3", 413 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 414 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 415 | "license": "MIT", 416 | "engines": { 417 | "node": ">= 0.4" 418 | }, 419 | "funding": { 420 | "url": "https://github.com/sponsors/ljharb" 421 | } 422 | }, 423 | "node_modules/hasown": { 424 | "version": "2.0.2", 425 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 426 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 427 | "license": "MIT", 428 | "dependencies": { 429 | "function-bind": "^1.1.2" 430 | }, 431 | "engines": { 432 | "node": ">= 0.4" 433 | } 434 | }, 435 | "node_modules/http-errors": { 436 | "version": "2.0.0", 437 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 438 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 439 | "license": "MIT", 440 | "dependencies": { 441 | "depd": "2.0.0", 442 | "inherits": "2.0.4", 443 | "setprototypeof": "1.2.0", 444 | "statuses": "2.0.1", 445 | "toidentifier": "1.0.1" 446 | }, 447 | "engines": { 448 | "node": ">= 0.8" 449 | } 450 | }, 451 | "node_modules/iconv-lite": { 452 | "version": "0.4.24", 453 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 454 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 455 | "license": "MIT", 456 | "dependencies": { 457 | "safer-buffer": ">= 2.1.2 < 3" 458 | }, 459 | "engines": { 460 | "node": ">=0.10.0" 461 | } 462 | }, 463 | "node_modules/inherits": { 464 | "version": "2.0.4", 465 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 466 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 467 | "license": "ISC" 468 | }, 469 | "node_modules/ipaddr.js": { 470 | "version": "1.9.1", 471 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 472 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 473 | "license": "MIT", 474 | "engines": { 475 | "node": ">= 0.10" 476 | } 477 | }, 478 | "node_modules/media-typer": { 479 | "version": "0.3.0", 480 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 481 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 482 | "license": "MIT", 483 | "engines": { 484 | "node": ">= 0.6" 485 | } 486 | }, 487 | "node_modules/merge-descriptors": { 488 | "version": "1.0.3", 489 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", 490 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", 491 | "license": "MIT", 492 | "funding": { 493 | "url": "https://github.com/sponsors/sindresorhus" 494 | } 495 | }, 496 | "node_modules/methods": { 497 | "version": "1.1.2", 498 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 499 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 500 | "license": "MIT", 501 | "engines": { 502 | "node": ">= 0.6" 503 | } 504 | }, 505 | "node_modules/mime": { 506 | "version": "1.6.0", 507 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 508 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 509 | "license": "MIT", 510 | "bin": { 511 | "mime": "cli.js" 512 | }, 513 | "engines": { 514 | "node": ">=4" 515 | } 516 | }, 517 | "node_modules/mime-db": { 518 | "version": "1.52.0", 519 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 520 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 521 | "license": "MIT", 522 | "engines": { 523 | "node": ">= 0.6" 524 | } 525 | }, 526 | "node_modules/mime-types": { 527 | "version": "2.1.35", 528 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 529 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 530 | "license": "MIT", 531 | "dependencies": { 532 | "mime-db": "1.52.0" 533 | }, 534 | "engines": { 535 | "node": ">= 0.6" 536 | } 537 | }, 538 | "node_modules/ms": { 539 | "version": "2.0.0", 540 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 541 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", 542 | "license": "MIT" 543 | }, 544 | "node_modules/nan": { 545 | "version": "2.20.0", 546 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", 547 | "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", 548 | "license": "MIT" 549 | }, 550 | "node_modules/negotiator": { 551 | "version": "0.6.3", 552 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 553 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 554 | "license": "MIT", 555 | "engines": { 556 | "node": ">= 0.6" 557 | } 558 | }, 559 | "node_modules/node-pty": { 560 | "version": "1.0.0", 561 | "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz", 562 | "integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==", 563 | "hasInstallScript": true, 564 | "license": "MIT", 565 | "dependencies": { 566 | "nan": "^2.17.0" 567 | } 568 | }, 569 | "node_modules/object-inspect": { 570 | "version": "1.13.2", 571 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 572 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", 573 | "license": "MIT", 574 | "engines": { 575 | "node": ">= 0.4" 576 | }, 577 | "funding": { 578 | "url": "https://github.com/sponsors/ljharb" 579 | } 580 | }, 581 | "node_modules/on-finished": { 582 | "version": "2.4.1", 583 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 584 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 585 | "license": "MIT", 586 | "dependencies": { 587 | "ee-first": "1.1.1" 588 | }, 589 | "engines": { 590 | "node": ">= 0.8" 591 | } 592 | }, 593 | "node_modules/parseurl": { 594 | "version": "1.3.3", 595 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 596 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 597 | "license": "MIT", 598 | "engines": { 599 | "node": ">= 0.8" 600 | } 601 | }, 602 | "node_modules/path-to-regexp": { 603 | "version": "0.1.10", 604 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", 605 | "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", 606 | "license": "MIT" 607 | }, 608 | "node_modules/proxy-addr": { 609 | "version": "2.0.7", 610 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 611 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 612 | "license": "MIT", 613 | "dependencies": { 614 | "forwarded": "0.2.0", 615 | "ipaddr.js": "1.9.1" 616 | }, 617 | "engines": { 618 | "node": ">= 0.10" 619 | } 620 | }, 621 | "node_modules/qs": { 622 | "version": "6.13.0", 623 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 624 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 625 | "license": "BSD-3-Clause", 626 | "dependencies": { 627 | "side-channel": "^1.0.6" 628 | }, 629 | "engines": { 630 | "node": ">=0.6" 631 | }, 632 | "funding": { 633 | "url": "https://github.com/sponsors/ljharb" 634 | } 635 | }, 636 | "node_modules/range-parser": { 637 | "version": "1.2.1", 638 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 639 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 640 | "license": "MIT", 641 | "engines": { 642 | "node": ">= 0.6" 643 | } 644 | }, 645 | "node_modules/raw-body": { 646 | "version": "2.5.2", 647 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 648 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 649 | "license": "MIT", 650 | "dependencies": { 651 | "bytes": "3.1.2", 652 | "http-errors": "2.0.0", 653 | "iconv-lite": "0.4.24", 654 | "unpipe": "1.0.0" 655 | }, 656 | "engines": { 657 | "node": ">= 0.8" 658 | } 659 | }, 660 | "node_modules/safe-buffer": { 661 | "version": "5.2.1", 662 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 663 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 664 | "funding": [ 665 | { 666 | "type": "github", 667 | "url": "https://github.com/sponsors/feross" 668 | }, 669 | { 670 | "type": "patreon", 671 | "url": "https://www.patreon.com/feross" 672 | }, 673 | { 674 | "type": "consulting", 675 | "url": "https://feross.org/support" 676 | } 677 | ], 678 | "license": "MIT" 679 | }, 680 | "node_modules/safer-buffer": { 681 | "version": "2.1.2", 682 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 683 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 684 | "license": "MIT" 685 | }, 686 | "node_modules/send": { 687 | "version": "0.19.0", 688 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", 689 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", 690 | "license": "MIT", 691 | "dependencies": { 692 | "debug": "2.6.9", 693 | "depd": "2.0.0", 694 | "destroy": "1.2.0", 695 | "encodeurl": "~1.0.2", 696 | "escape-html": "~1.0.3", 697 | "etag": "~1.8.1", 698 | "fresh": "0.5.2", 699 | "http-errors": "2.0.0", 700 | "mime": "1.6.0", 701 | "ms": "2.1.3", 702 | "on-finished": "2.4.1", 703 | "range-parser": "~1.2.1", 704 | "statuses": "2.0.1" 705 | }, 706 | "engines": { 707 | "node": ">= 0.8.0" 708 | } 709 | }, 710 | "node_modules/send/node_modules/encodeurl": { 711 | "version": "1.0.2", 712 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 713 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 714 | "license": "MIT", 715 | "engines": { 716 | "node": ">= 0.8" 717 | } 718 | }, 719 | "node_modules/send/node_modules/ms": { 720 | "version": "2.1.3", 721 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 722 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 723 | "license": "MIT" 724 | }, 725 | "node_modules/serve-static": { 726 | "version": "1.16.2", 727 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", 728 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", 729 | "license": "MIT", 730 | "dependencies": { 731 | "encodeurl": "~2.0.0", 732 | "escape-html": "~1.0.3", 733 | "parseurl": "~1.3.3", 734 | "send": "0.19.0" 735 | }, 736 | "engines": { 737 | "node": ">= 0.8.0" 738 | } 739 | }, 740 | "node_modules/set-function-length": { 741 | "version": "1.2.2", 742 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 743 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 744 | "license": "MIT", 745 | "dependencies": { 746 | "define-data-property": "^1.1.4", 747 | "es-errors": "^1.3.0", 748 | "function-bind": "^1.1.2", 749 | "get-intrinsic": "^1.2.4", 750 | "gopd": "^1.0.1", 751 | "has-property-descriptors": "^1.0.2" 752 | }, 753 | "engines": { 754 | "node": ">= 0.4" 755 | } 756 | }, 757 | "node_modules/setprototypeof": { 758 | "version": "1.2.0", 759 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 760 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 761 | "license": "ISC" 762 | }, 763 | "node_modules/side-channel": { 764 | "version": "1.0.6", 765 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 766 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 767 | "license": "MIT", 768 | "dependencies": { 769 | "call-bind": "^1.0.7", 770 | "es-errors": "^1.3.0", 771 | "get-intrinsic": "^1.2.4", 772 | "object-inspect": "^1.13.1" 773 | }, 774 | "engines": { 775 | "node": ">= 0.4" 776 | }, 777 | "funding": { 778 | "url": "https://github.com/sponsors/ljharb" 779 | } 780 | }, 781 | "node_modules/statuses": { 782 | "version": "2.0.1", 783 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 784 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 785 | "license": "MIT", 786 | "engines": { 787 | "node": ">= 0.8" 788 | } 789 | }, 790 | "node_modules/toidentifier": { 791 | "version": "1.0.1", 792 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 793 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 794 | "license": "MIT", 795 | "engines": { 796 | "node": ">=0.6" 797 | } 798 | }, 799 | "node_modules/type-is": { 800 | "version": "1.6.18", 801 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 802 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 803 | "license": "MIT", 804 | "dependencies": { 805 | "media-typer": "0.3.0", 806 | "mime-types": "~2.1.24" 807 | }, 808 | "engines": { 809 | "node": ">= 0.6" 810 | } 811 | }, 812 | "node_modules/typescript": { 813 | "version": "5.6.2", 814 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", 815 | "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", 816 | "license": "Apache-2.0", 817 | "bin": { 818 | "tsc": "bin/tsc", 819 | "tsserver": "bin/tsserver" 820 | }, 821 | "engines": { 822 | "node": ">=14.17" 823 | } 824 | }, 825 | "node_modules/undici-types": { 826 | "version": "5.26.5", 827 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 828 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 829 | "license": "MIT" 830 | }, 831 | "node_modules/unpipe": { 832 | "version": "1.0.0", 833 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 834 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 835 | "license": "MIT", 836 | "engines": { 837 | "node": ">= 0.8" 838 | } 839 | }, 840 | "node_modules/utils-merge": { 841 | "version": "1.0.1", 842 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 843 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 844 | "license": "MIT", 845 | "engines": { 846 | "node": ">= 0.4.0" 847 | } 848 | }, 849 | "node_modules/vary": { 850 | "version": "1.1.2", 851 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 852 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 853 | "license": "MIT", 854 | "engines": { 855 | "node": ">= 0.8" 856 | } 857 | }, 858 | "node_modules/ws": { 859 | "version": "7.5.10", 860 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", 861 | "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", 862 | "license": "MIT", 863 | "engines": { 864 | "node": ">=8.3.0" 865 | }, 866 | "peerDependencies": { 867 | "bufferutil": "^4.0.1", 868 | "utf-8-validate": "^5.0.2" 869 | }, 870 | "peerDependenciesMeta": { 871 | "bufferutil": { 872 | "optional": true 873 | }, 874 | "utf-8-validate": { 875 | "optional": true 876 | } 877 | } 878 | } 879 | } 880 | } 881 | -------------------------------------------------------------------------------- /web_netcat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@types/node": "^18.11.8", 4 | "express": "^4.21.0", 5 | "express-ws": "^5.0.2", 6 | "node-pty": "^1.0.0", 7 | "typescript": "^5.6.2", 8 | "@xterm/xterm": "^5.5.0", 9 | "@xterm/addon-attach": "^0.11.0", 10 | "@xterm/addon-fit": "^0.10.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /web_netcat/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |