Skip to content

goboxd: Team Sudo — Baratam Praneeth Gupta (25F2007012)Team/sudo - #11

Open
Praneeth0910 wants to merge 139 commits into
thesouldev:masterfrom
Praneeth0910:team/sudo
Open

goboxd: Team Sudo — Baratam Praneeth Gupta (25F2007012)Team/sudo#11
Praneeth0910 wants to merge 139 commits into
thesouldev:masterfrom
Praneeth0910:team/sudo

Conversation

@Praneeth0910

Copy link
Copy Markdown

Submission Details

Participant: Baratam Praneeth Gupta
Roll number: 25F2007012
Team: Sudo
Branch: team/sudo
Commits: 120
Go experience at start: None


Refer getting-started.md and follow it step by step for installation if you are beginner

Quickstart

Make sure that docker desktop is running before

git clone https://github.com/Praneeth0910/goboxd -b team/sudo
cd goboxd
make build        # ~3.5 min, nsjail compiled from source at tag 3.4 (Sometimes may take longer wait patiently!!)
make run          # starts on :8080
make test         # 33 unit tests, no Docker required
make integration  # 138 integration tests against live container
make load         # hey benchmark at 1/10/50/100 concurrent clients
make lint         # golangci-lint v1.64.8, CI green

After make run you can use the special feature Interactive UI (Refer getting-started.md line 274)

What I built

An HTTP daemon in Go that accepts source code via POST /run, compiles
it if needed, executes it inside an nsjail sandbox, and returns
structured per-test-case results with status codes matching the spec
vocabulary exactly.

The language registry is a YAML file. Adding a language requires one
YAML block and one Dockerfile apt line — zero Go code changes. Verified
by adding rust, go, kotlin, and ruby during development without
touching any Go source.

Languages (11): py3, cpp, c, java, node, bash, verilog, rust, go,
kotlin, ruby — all verified working end-to-end with real compilation
and execution inside nsjail.

Bonus languages (4 beyond required 7): rust, go, kotlin, ruby.

Endpoints: POST /run, GET /healthz, GET /readyz, GET /info

Concurrency: buffered channel semaphore, default runtime.NumCPU()
slots. Requests beyond capacity return HTTP 429 queue_full after a
configurable timeout (default 30s) rather than queuing indefinitely.

chi router: I have chosen it over net/http for its composable middleware
chain and clean route parameter handling. chi adds zero non-stdlib
dependencies beyond the router itself, and its middleware stack made
CORS, structured logging, and request-ID injection cleanly separable
from handler logic.

nsjail: built from source at tag 3.4 in a separate Docker build
stage on debian:trixie-slim (GLIBC 2.41). PID, mount, UTS, IPC, net
namespaces + seccomp Kafel policy. Per-request wall time, memory, and
process limits from languages.yaml. Cgroup memory tracking reports
actual peak RSS per test case.


Security holes closed (10 total, 7 required)

# Hole Fix Location
1 Path traversal via filename Reject separators, dots, control chars internal/validate/filename.go:34
2 Shell-style dir commands os.MkdirTemp/RemoveAll, no shell invocation internal/sandbox/dir.go:28
3 Compiler flag injection Per-language allowlist with glob matching internal/validate/flags.go:19
4 No request size limits http.MaxBytesReader + CapReader internal/handler/run.go:89
5 UID collisions under load Atomic counter + PID + random hex internal/sandbox/dir.go:18
6 Unbounded child output CapReader with truncation marker internal/runner/runner.go:312
7 Stale jail directories Startup sweep + 10-minute ticker internal/runner/sweep.go
8 Slowloris / slow-body ReadTimeout 15s, ReadHeaderTimeout 5s cmd/goboxd/main.go:113
9 Symlink TOCTOU O_EXCL|O_NOFOLLOW + Lstat verification internal/runner/runner.go:358
10 Queue starvation DoS HTTP 429 queue_full with timeout internal/handler/run.go:187

Additional hardening beyond spec: SafeJoin for all path construction
inside the jail, seccomp Kafel policy restricting syscalls, cgroup
memory hard limits with peak reporting.

Full audit with before/after analysis in docs/security.md.


Tests

33 unit tests across config loading, filename validation (33
table-driven cases), flag validation (41 table-driven cases), status
mapping, output comparison, semaphore behaviour, and CapReader
truncation. All pass with -race -count=1.

138 integration tests against a live container. Covered all
endpoints, all 9 status codes, security boundaries (path traversal,
flag injection, oversized body, null bytes, control chars), concurrency
under load, fork bomb, infinite output, segfault, memory hog, TLE, and
stdin isolation across test cases. Run with make integration
Makefile handles container lifecycle automatically.


Benchmarks (Python 3, WSL2, 4 cores, nsjail enabled)

Clients p50 p95 p99 RPS Errors
1 32ms 39ms 41ms 30 0
10 193ms 255ms 283ms 51 0
50 919ms 1124ms 1155ms 50 0
100 1740ms 1928ms 2006ms 53 0

Zero errors at all concurrency levels for interpreted languages. C++
timeout behaviour under CPU contention documented with root cause
analysis in docs/benchmarks.md.


CI

GitHub Actions on every push to team/sudo: lint (golangci-lint v1.64.8,
20+ linters), build (go build), test (go test -race), Docker build
including toolchain smoke tests for all 11 languages. All four jobs are
currently green.


Docs

docs/ — api.md, architecture.md, security.md, benchmarks.md,
languages.md, testing.md, logging.md, how-to-use.md, getting-started.md,
development.md

docs/ai/ — prompts.md, adrs.md, issues.md, plan-evolution.md,
patterns.md, postmortem.md. The nsjail chroot coordinate system bug
and fork bomb pipe leak are documented in detail in issues.md.

docs/demo/index.html — browser IDE using Monaco Editor. Language
list loads live from GET /info. Test cases connect directly to
POST /run. Open while the container is running.

…g validation

- Added table_driven_test.go with 74 sub-tests (33 filename + 41 flags)
- 33 ValidateFilename test cases covering path traversal, injection, valid names
- 41 ValidateFlags test cases covering compiler injection, glob matching, allowlist
- Benchmark tests showing 7.6M ops/sec for filename, 1.9M for flags
- Added comprehensive testing.md documentation
- All 100 tests passing (27 test functions + 74 table-driven cases)

Security Coverage:
- Path traversal (../../etc/passwd)
- Absolute paths (/etc/passwd)
- Hidden files (.bashrc, .ssh)
- Control characters and null bytes
- Compiler flag injection (-fplugin, @file, --specs)
- Linker attacks (-Wl,-rpath)
- Response file injection
- Glob pattern matching (-std=*)
Copilot AI review requested due to automatic review settings May 31, 2026 05:33
@thesouldev

Copy link
Copy Markdown
Owner

Evaluation Summary

Team: pr11-sudo · Rank: 3 / 45 · Weighted score: 75.7 / 100

Area (weight) Score What it covers
Technical (~60%) 82.8% API contract, security holes, concurrency + benchmarks, plug-and-play languages
Code quality / SDLC (~30%) 57.1% tests, clean git history, lint, docs
Communication (~10%) 83.3% README clarity, framework justification

Checks:

  • Build: Yes
  • Health: Yes
  • CI: other-commit:success
  • Sandbox: nsjail
  • Happy-path: 6/9

Notes: framework go-chi/chi (verify justification in README)

Thanks for participating! Scores reflect evaluation against the spec. Reply here if you have questions.

@Praneeth0910

Copy link
Copy Markdown
Author

@thesouldev , just a small clarification!! In the website my work is ranked as 2nd and here it is 3rd, can you please clarify this asap!!
image
Screenshot 2026-06-08 171545

@thesouldev

Copy link
Copy Markdown
Owner

@thesouldev , just a small clarification!! In the website my work is ranked as 2nd and here it is 3rd, can you please clarify this asap!!

image Screenshot 2026-06-08 171545

One of the submission was internal so had to exclude from the finalist list

@Praneeth0910

Copy link
Copy Markdown
Author

Hey, thanks for the earlier clarification!! Now as phase 2 / stage 2 time period got over, what do you expect us to do now for stage 2 and can you brief out what should we need to do for stage 2. Also can i now modify the code and push it again or its not allowed. Kindly resolve these doubts asap

- Replace normalizeWhitespace (strings.Fields join) with strings.TrimSpace
- Internal whitespace differences (e.g. 'hello  world' vs 'hello world')
  now correctly return wrong_output instead of output_whitespace_mismatch
- Remove inline TrimRight comparison in runTestCase; route all output
  comparison through status.CompareOutput for a single canonical path
- Delete normalizeWhitespace helper entirely
…too_large reachable

- Add MaxBodyBytes = 4 MiB to Config (body cap for entire HTTP request)
- MaxSourceBytes = 256 KiB is now checked separately after JSON decode
- http.MaxBytesReader now uses MaxBodyBytes, so a 300 KiB source inside
  a 301 KiB body no longer triggers invalid_json from the reader
- After successful JSON decode, len(req.Source) > MaxSourceBytes returns
  source_too_large (was previously unreachable)
- Add max_body_bytes: 4194304 to languages.yaml
Add TestArtifactPlaceholderResolution confirming that:
- ./{{artifact}} with artifactFilename=solution → ./solution
  (correct for nsjail chroot=jailDir, where ./solution = jail root binary)
- bare {{artifact}} → solution
- /usr/bin/{{artifact}} with solution.vvp → /usr/bin/solution.vvp
- cmd with no placeholder is untouched

All 7 sub-tests PASS. No code change was required — placeholder
resolution was already correct in runTestCase.
…o Dockerfile edit

Restructure runtime stage to iterate over scripts/lang_install/*.sh:
  for f in /tmp/lang_install/*.sh; do bash "$f" || exit 1; done

Adding a new language now requires only:
  1. One YAML block in languages.yaml
  2. One shell script in scripts/lang_install/<id>.sh
  Zero Dockerfile changes. Zero Go code changes.

Scripts added (11 languages):
  py3.sh, cpp.sh, c.sh, java.sh, bash.sh, javascript.sh,
  verilog.sh, rust.sh, go.sh (no-op, binary from builder),
  kotlin.sh, ruby.sh

Smoke-test RUN block retained at end of Dockerfile to fail
loudly if any toolchain is missing after the script loop.
- Add scripts/lang_install/lua.sh (installs lua5.4 via apt)
- Add 'lua' language block to languages.yaml:
    source_filename: solution.lua
    run.cmd: /usr/bin/lua5.4 {{source}}
    limits: wall_time_s=9, memory_kb=102400
- Add lua5.4 -v to Dockerfile smoke-test block
- Demo: lua print("hello") → accepted (no build step)

Lua is the 5th bonus language (existing: rust, go, kotlin, ruby).
Each bonus language passing /readyz = +1 point in Stage 2 scoring.
Add three new ADRs:
  ADR-002: Per-language install scripts (no Dockerfile change to add a language)
  ADR-003: Separate MaxBodyBytes from MaxSourceBytes (source_too_large reachable)
  ADR-004: Whole-string TrimSpace for output_whitespace_mismatch

Add Stage 2 Fixes postmortem section documenting:
  - normalizeWhitespace was too aggressive (strings.Fields collapses internal whitespace)
  - source_too_large was unreachable (MaxSourceBytes used as body cap)
  - Dockerfile restructure for plug-and-play language model
  - What I'd do differently (write status tests first)
languages.md (was empty):
  - Full language table: 12 languages (11 original + Lua)
  - Default resource limits per language
  - Example code snippets for every language
  - Instructions for adding a new language (one YAML + one .sh)

benchmarks.md:
  - Stage 2 update section: all fixes have zero runtime perf impact
  - Lua expected benchmark profile (similar to py3)
  - Source size limit verification commands
  - Stage 1 numbers remain valid
Fix two pre-existing TestCompareOutput test cases that encoded the old
normalizeWhitespace (strings.Fields) semantics:
  - 'multiple spaces between words': 'hello    world' vs 'hello world'
    was: output_whitespace_mismatch (wrong — normalizeWhitespace collapsed)
    now: wrong_output (correct — internal whitespace differs)
  - 'multiple newlines': 'hello\n\n\nworld\n' vs 'hello\nworld'
    was: output_whitespace_mismatch (wrong — normalizeWhitespace collapsed)
    now: wrong_output (correct — internal newlines differ)

Both test cases now reflect the spec: only leading/trailing whitespace
trimmed by strings.TrimSpace determines output_whitespace_mismatch.

Verification:
  go vet ./...             PASS
  go build ./...           PASS
  go test -race ./...      PASS (5 packages, all ok)
scripts/ was excluded globally in .dockerignore, which caused:
  CopyIgnoredFile: Attempting to Copy file "scripts/lang_install"
  that is excluded by .dockerignore (line 40)

scripts are reachable by the COPY in the Dockerfile runtime stage.
the actual scripts within the lang_install directory, fixing the build issue.
The 'go version' command in go.sh failed because the per-language
scripts are executed before the COPY --from=go-builder step in the
Dockerfile.

Go is already verified at the end of the Dockerfile in the smoke test
block, so go.sh is now a true no-op.
…ft installations, and add automated load testing tools.
@thesouldev
thesouldev requested review from thesouldev and removed request for thesouldev June 18, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants