🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

practicode

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

practicode - npm Package Compare versions

Comparing version
0.1.17
to
0.1.18
+9
.dockerignore
.git
.practicode
.pytest_cache
.superpowers
.venv
node_modules
problems
submissions
target
FROM node:24-bookworm-slim AS node
FROM rust:1-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates python3 default-jdk-headless \
&& rm -rf /var/lib/apt/lists/*
COPY --from=node /usr/local/bin/node /usr/local/bin/node
WORKDIR /opt/practicode
COPY Cargo.toml Cargo.lock ./
COPY src ./src
COPY assets ./assets
RUN cargo build --release --locked \
&& install -m 0755 target/release/practicode /usr/local/bin/practicode \
&& rm -rf target /usr/local/cargo/registry /usr/local/cargo/git
ENV HOME=/tmp
ENV PATH=/usr/local/cargo/bin:$PATH
ENV PRACTICODE_NO_UPDATE_CHECK=1
WORKDIR /workspace
ENTRYPOINT ["practicode"]
+102
-1

@@ -8,2 +8,4 @@ #!/usr/bin/env node

const root = path.resolve(__dirname, "..");
const packageJson = require(path.join(root, "package.json"));
const dockerImage = `practicode-sandbox:${packageJson.version}`;
const exe = path.join(

@@ -15,2 +17,3 @@ root,

);
const args = process.argv.slice(2);

@@ -34,2 +37,100 @@ function rustInstallCommand() {

function dockerInstallCommand() {
if (process.platform === "win32") {
return "winget install -e --id Docker.DockerDesktop";
}
if (process.platform === "darwin") {
return "brew install --cask docker";
}
return "Ubuntu: https://docs.docker.com/engine/install/ubuntu/ | Debian: https://docs.docker.com/engine/install/debian/";
}
function printDockerHelp() {
console.error("practicode: Docker is required for --docker sandbox mode.");
console.error(`Install Docker: ${dockerInstallCommand()}`);
console.error("Start Docker, then run practicode --docker again.");
console.error("More options: https://docs.docker.com/get-docker/");
}
function runDockerSandbox(forwardedArgs) {
const version = spawnSync("docker", ["version"], { stdio: "ignore" });
if (version.error || version.status !== 0) {
if (version.error) {
console.error(`practicode: failed to run docker: ${version.error.message}`);
}
printDockerHelp();
process.exit(1);
}
const build = spawnSync("docker", ["build", "-t", dockerImage, root], {
stdio: "inherit",
});
if (build.error) {
console.error(`practicode: failed to build Docker sandbox: ${build.error.message}`);
printDockerHelp();
process.exit(1);
}
if (build.status !== 0) {
console.error("practicode: Docker sandbox build failed.");
printDockerHelp();
process.exit(build.status ?? 1);
}
const runArgs = [
"run",
"--rm",
process.stdin.isTTY ? "-it" : "-i",
"--init",
"--network",
"none",
"--cpus",
"2",
"--memory",
"1g",
"--pids-limit",
"256",
"--cap-drop",
"ALL",
"--security-opt",
"no-new-privileges",
"--read-only",
"--tmpfs",
"/tmp:rw,nosuid,nodev,size=256m,mode=1777",
"--mount",
`type=bind,source=${process.cwd()},target=/workspace`,
"-w",
"/workspace",
"-e",
`TERM=${process.env.TERM || "xterm-256color"}`,
"-e",
"HOME=/tmp",
"-e",
"PRACTICODE_NO_UPDATE_CHECK=1",
];
if (process.env.COLORTERM) {
runArgs.push("-e", `COLORTERM=${process.env.COLORTERM}`);
}
if (typeof process.getuid === "function" && typeof process.getgid === "function") {
runArgs.push("--user", `${process.getuid()}:${process.getgid()}`);
}
runArgs.push(dockerImage, ...forwardedArgs);
const run = spawnSync("docker", runArgs, {
cwd: process.cwd(),
stdio: "inherit",
});
if (run.error) {
console.error(`practicode: failed to run Docker sandbox: ${run.error.message}`);
printDockerHelp();
process.exit(1);
}
process.exit(run.status ?? 1);
}
const dockerIndex = args.indexOf("--docker");
if (dockerIndex !== -1) {
args.splice(dockerIndex, 1);
runDockerSandbox(args);
}
if (!existsSync(exe)) {

@@ -52,3 +153,3 @@ const build = spawnSync(

const run = spawnSync(exe, process.argv.slice(2), {
const run = spawnSync(exe, args, {
cwd: process.cwd(),

@@ -55,0 +156,0 @@ stdio: "inherit",

+1
-1

@@ -456,3 +456,3 @@ # This file is automatically @generated by Cargo.

name = "practicode"
version = "0.1.17"
version = "0.1.18"
dependencies = [

@@ -459,0 +459,0 @@ "anyhow",

[package]
name = "practicode"
version = "0.1.17"
version = "0.1.18"
edition = "2024"

@@ -5,0 +5,0 @@ description = "Local-first coding-test practice in a Rust terminal UI with optional AI help."

{
"name": "practicode",
"version": "0.1.17",
"version": "0.1.18",
"description": "Local-first coding-test practice in a Rust terminal UI with optional AI help.",

@@ -30,2 +30,4 @@ "license": "MIT",

"Cargo.toml",
"Dockerfile",
".dockerignore",
"LICENSE",

@@ -32,0 +34,0 @@ "README.md",

@@ -129,2 +129,33 @@ # practicode

### Docker sandbox
If you do not want submissions to run directly on your host, use the npm launcher sandbox:
```bash
practicode --docker
```
The launcher builds a local `practicode-sandbox:<version>` image, then runs the TUI in Docker with the current directory mounted at `/workspace`. The container runs without network access, with a read-only root filesystem, a writable `/tmp`, dropped Linux capabilities, `no-new-privileges`, and CPU/memory/process limits. Your current directory is still writable so `.practicode/`, `problems/`, and `submissions/` can be saved.
Install Docker first if needed:
```bash
# macOS
brew install --cask docker
# Windows
winget install -e --id Docker.DockerDesktop
# Ubuntu / Debian
# Use Docker's official apt repository:
# Ubuntu: https://docs.docker.com/engine/install/ubuntu/
# Debian: https://docs.docker.com/engine/install/debian/
```
After starting Docker, check the sandbox:
```bash
practicode --docker --smoke
```
## Update

@@ -206,2 +237,3 @@

- `/run` executes your local submission as a normal process. It is not an OS sandbox.
- `practicode --docker` runs the TUI and judge in a restricted Docker container, but Docker is still a shared-kernel container runtime, not a guarantee against every escape.
- `/run` scrubs inherited environment variables and hides case input/expected output in failure logs.

@@ -208,0 +240,0 @@ - `/hint`, AI-backed `/next`, and `/generate` send the current problem/submission context to the selected provider CLI.