The live demo (https://mr-ans-2006.github.io/SindhiScript/) runs visitors'
SindhiScript code in their own browser. This file lists the threats that
creates and how the demo defends against each one. The demo's code lives
in docs/; its interpreter in docs/interpreter/سنڌي/ is a copy of the main
package with the limits below added.
- T1 — Hang: a visitor writes an infinite loop and freezes their own tab.
- T2 — Memory blowup: unbounded list/dict construction, deep recursion, giant powers or string/list repetition crash the tab.
- T3 — Output injection (XSS): program output contains
<script>or HTML and the page renders it as HTML. - T4 — Pyodide surface creep: the import / Python-interop block reaches
browser APIs (
js,pyodide.http,localStorage,document). - T5 — Supply chain: a compromised CDN or version drift in Pyodide;
tampered bundled
.سنexamples. - T6 — Module loader path traversal: a
.سنimport reads arbitrary files from Pyodide's filesystem.
- T1 — Worker + two limits. The interpreter runs in
worker.js, never on the UI thread. A step budget (300,000 steps, counted in the evaluator'sexecute/evaluatedispatch) raises a graceful Sindhi "step limit exceeded" error. A 5-secondworker.terminate()on the UI thread is the hard backstop, and Reset starts a fresh worker. An infinite loop stops in about 0.6 s. - T2 — Collection ceilings + recursion caps. Lists/dicts are refused
above 1,000,000 elements (literals,
حدranges, append/extend, conversions); string/list repetition and integer powers are guarded by result-size estimates. Sindhi function calls are capped at 400 frames (~2,400 Python frames), and Python's own recursion limit is set to 3,000 as a backstop for recursion the Sindhi cap cannot see (e.g. deeply nested parentheses in the parser). 10,000-frame recursion and 10,000 nested parentheses both end in a clean Sindhi error, not a crash. Program output is capped at 1,000,000 characters. - T3 — Plain-text output only. The worker returns output as text and
the page renders it with
textContentinto a<pre>. The demo's own code uses noinnerHTML,eval(,new Functionordocument.write. The Content-Security-Policy below blocks script injection as a second layer. - T4 — Sandboxed interop. Python interop (
ٻاهري ڪم) runs under AST validation, restricted builtins and an import allowlist of exactlymath,random,jsonanddatetime. Blocked: every other import (os,sys,subprocess,importlib,js,pyodide.*,platform, …), dunder attribute access, and dangerous names (eval,exec,open,compile,__import__,getattr, …). The file builtins (فائل_پڙهيو/فائل_لکيو) are disabled. The worker never touches browser storage or the DOM. - T5 — Pinned, self-hosted dependencies. No CDN and no third-party
scripts at runtime:
- Pyodide 0.29.5 (Python 3.13.2), from the official
pyodide-core-0.29.5.tar.bz2release asset (SHA-256ab08aafc11a3c9a0fe73825eb2cec78bda9464348dd17414e7ec0132ef3451e8), hosted indocs/pyodide/. - Vazirmatn v33.003 font (
Vazirmatn-Regular.woff2,Vazirmatn-Bold.woff2), hosted indocs/fonts/. - The example picker fetches only the four bundled examples, same-origin.
- Pyodide 0.29.5 (Python 3.13.2), from the official
- T6 — In-memory modules only. The demo's module loader never touches
the filesystem: modules load only from an in-memory dict of the bundled
examples, and names containing
/,\,..or absolute paths are rejected before lookup.
default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'none'
'wasm-unsafe-eval' is required: Pyodide cannot compile its WebAssembly
without it. It allows WebAssembly compilation only, not JavaScript eval.
The page has no inline <script> or <style>; all page logic lives in
app.js and style.css.
python test_user_code.py runs the demo's security checks (step limit,
recursion cap, collection ceiling, HTML output, blocked imports, module
path traversal) against docs/demo_run.py.
Found a way around these limits? Please open an issue on this repository.