P0 hardening: sandbox exec, fix transpiler bugs, add full test suite + CI#2
Merged
Merged
Conversation
Introduces the formal audit artifacts that drive the rest of this change set: 16 findings across security, license, code quality, packaging, and compliance, prioritized P0-P3. The follow-up commits on this branch close every P0 item. Also adds scripts/create_issues.sh for pushing the backlog into GitHub Issues, and updates .gitignore to exclude pytest/coverage artifacts and local Claude Code config. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the two placeholders that made the previous LICENSE legally non-functional: - "Copyright (c) 2025 [Your Name]" -> "Copyright (c) 2025 Bishwas Jha" - "[MIT terms continued...]" -> full MIT grant + warranty disclaimer Closes AUDIT_REPORT.md finding LIC-001 (CRITICAL): until now the project had no enforceable license and could not legally be used or redistributed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces naive str.replace() with a two-pass algorithm that splits
input into (string-literal, code) regions and only applies keyword
substitution to code regions, using custom lookbehind/lookahead for
the Devanagari block so keywords embedded in longer identifiers are
not matched.
Fixes two data-corrupting bugs proven in AUDIT_REPORT.md finding
CQ-001 (CRITICAL):
- छपाउ("यह में है") no longer becomes print("यह in है")
(the word में inside the string literal is preserved)
- नवयुग = ५ no longer becomes __init__युग = 5
(नव -> __init__ only fires at word boundaries)
The pre-compiled pattern cache keeps the hot path fast despite the
regex overhead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two false-positive fixes that blocked idiomatic Maithili code from
linting cleanly:
1. Comparison operators (==, !=, <=, >=) were being parsed as
assignments by the naive .split("=") logic, producing spurious
"invalid variable name" errors on any conditional. Now detects
the operator context before treating an = as an assignment.
2. नव (mapped to __init__) was flagged as an unused function
because it is never called by name — Python invokes it
implicitly on class instantiation. Skipped from the unused-
function post-scan.
Also ignores attribute assignments (e.g., स्वयं.नाम = ...) which
are never new-variable declarations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes AUDIT_REPORT.md finding SEC-001 (CRITICAL): previously any
.dmai file could do "आयात os" (which transpiled to "import os")
and then perform arbitrary filesystem or process operations with
the full authority of the CLI user.
The new execution model:
- exec_globals no longer inherits the CLI's globals(); it gets a
fresh {"__builtins__": safe_builtins} dict.
- safe_builtins excludes __import__, exec, eval, compile, open,
and breakpoint. __import__ is replaced with a whitelist-only
variant that only permits modules in MAITHILI_MODULES values.
- translate_imports() now returns the set of modules it actually
translated so raw Python "import os" lines injected post-
transpile (never surfaced from a Maithili import) are rejected.
- _validate_imports() catches the rejected imports BEFORE exec
runs, since Python's import statement does not always route
through __import__.
Belt-and-suspenders: both layers (static validator + runtime
__import__ hook) must pass for a module to load, so bypassing one
still leaves the other in place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n -m entry
Wraps several packaging and CLI-ergonomics improvements that the
audit and tests both depend on:
- Adds pyproject.toml (PEP 517/518) as the canonical metadata source,
with pytest + coverage configuration co-located (closes PKG-001).
- setup.py kept as legacy shim; reads version via regex (not import)
so it works in isolated build envs. Bumps python_requires from
>=3.6 (EOL) to >=3.9, aligning with the CI matrix.
- maithili_dsl/__init__.py now exposes __version__ = "0.3.0" and
__all__ (closes PKG-002, PKG-003). Single source of truth.
- Adds maithili_dsl/__main__.py so `python -m maithili_dsl <file>`
works without relying on the installed console script.
- cli.main() now returns an integer exit code, accepts argv for
testability, and supports --version / -V. run_dmai_file() uses
named EXIT_* constants (NOT_FOUND, LINT_ERROR, IMPORT_BLOCKED,
RUNTIME_ERROR, USAGE) so CI and smoke tests can assert on
specific failure paths instead of just "non-zero".
- requirements-dev.txt pins pytest + pytest-cov + pytest-timeout
with compatible-range bounds.
- .gitignore ignores .venv/ (in addition to the previously added
pytest/coverage/claude entries).
No runtime dependencies added — the package still has zero runtime
deps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tiers
Closes AUDIT_REPORT.md finding CQ-002 (HIGH): the project had zero
automated tests. This commit adds 142 tests across 6 files:
- tests/test_numeral.py — every Devanagari digit + mixed text
- tests/test_transpile.py — every keyword in DEVNAGIRI_KEYWORD_MAP
parametrized + explicit CQ-001 regression cases (string literals
and word boundaries) + "transpiled output is valid Python"
- tests/test_linter.py — clean-code pass, == not flagged,
नव constructor not flagged as unused, unbalanced paren/quote,
line length, snake_case toggle, all known exception translations
- tests/test_security.py — _validate_imports rejects raw
Python imports; _make_safe_import only permits whitelist;
exec/eval/open/compile/breakpoint absent from safe_builtins;
end-to-end attacks (import os, exec(), eval(), open()) rejected
by run_dmai_file with correct exit codes
- tests/test_cli.py — every EXIT_* code is reachable by
a test; subprocess invocation via `python -m maithili_dsl`
works for --version, usage, and file execution
- tests/test_smoke.py — every example in examples/ runs
end-to-end (parametrized); error.dmai is expected to be
rejected by the linter, not executed
Coverage (measured locally with pytest-cov):
maithili_dsl/__init__.py 100%
maithili_dsl/cli.py 98%
maithili_dsl/transpiler/__init__.py 100%
maithili_dsl/transpiler/linter.py 90%
maithili_dsl/transpiler/mappings.py 100%
maithili_dsl/transpiler/numeral.py 100%
maithili_dsl/transpiler/transpile.py 98%
TOTAL 95%
Critical modules (cli, transpile, linter) all at or above the
90% gate set in the implementation plan.
Run with: pytest -v --cov=maithili_dsl --cov-report=term-missing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes AUDIT_REPORT.md finding #6 (P1): the project had no CI/CD
gate — code could merge without any automated verification.
The workflow runs on every push and PR to main and development:
- Matrix: 4 Python versions x 2 operating systems = 8 jobs.
- Installs the package editable + dev deps with pip caching keyed
on requirements-dev.txt + pyproject.toml.
- Verifies both CLI entry points (`python -m maithili_dsl` and the
`python_maithili` console script) respond to --version.
- Runs the full pytest suite with coverage.
- Runs a bash smoke loop over every .dmai in examples/, treating
error.dmai as expected-to-fail so a regression that starts
executing it would be caught immediately.
- Uploads coverage.xml as an artifact from the ubuntu + 3.12 job.
Also adds .github/PULL_REQUEST_TEMPLATE.md to enforce test evidence
and audit-reference fields on future PRs — so the thing that's in
the root of this branch (a PR body tied to specific BACKLOG items)
becomes the default for everyone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… CONTRIBUTING
Closes four compliance / docs findings from AUDIT_REPORT.md:
- SEC-003 / compliance "SECURITY.md MISSING": adds SECURITY.md with
a responsible-disclosure contact, supported-version table, an
explicit threat model pinned to the sandbox's enforcement layers,
and an SLA for acknowledgement / triage / patch.
- DOCS P3 "README references `youruser`": clone URL now points at
alphacrack/python-maithili-dsl. Also adds a Security section
linking to SECURITY.md, a Testing section documenting the
pytest + coverage-gate workflow, and moves "Unit testing + CI/CD"
out of Future Goals (it ships in this release). Replaces the
future-goals list with still-open items (while / try-except /
mypy).
- DOCS P2 "CONTRIBUTING.md has duplicated README content": the
entire README was pasted above the actual contributing section.
Rewrites CONTRIBUTING.md from scratch with real dev setup
(venv + editable install + requirements-dev), a test-tier table,
the coverage gate, and a security-change policy requiring a new
test + a "Security Considerations" PR section.
- Adds CHANGELOG.md in Keep-a-Changelog format. The 0.3.0 section
documents every Added / Fixed / Changed item on this branch,
cross-referenced to the AUDIT_REPORT.md finding IDs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes every P0 (CRITICAL) finding from AUDIT_REPORT.md and establishes the testing + CI infrastructure to prevent regression. Nine logical commits on top of
development.exec()runs withglobals()— sandbox escape[Your Name]/[MIT terms continued...]placeholdersSECURITY.mdpyproject.toml__version__, no public API==and onनवconstructoryouruserplaceholder, CONTRIBUTING duplicates READMEWhat changed
maithili_dsl/cli.py):exec()now runs against a fresh__builtins__dict (noexec/eval/open/compile/breakpoint/ raw__import__). Imports go through both a static validator (_validate_imports) and a runtime__import__wrapper, both enforcing theMAITHILI_MODULESwhitelist.maithili_dsl/transpiler/transpile.py): input is tokenized into (string-literal, code) regions; keyword substitution only touches code regions, with Devanagari-aware negative lookbehind/lookahead soनवयुगis preserved andनवonly fires at word boundaries.maithili_dsl/transpiler/linter.py): comparison operators (==/!=/<=/>=) are distinguished from assignments;नव(constructor) is skipped by the unused-function post-scan; attribute assignments (स्वयं.नाम = ...) are skipped.main()returns an integer exit code; namedEXIT_*constants so CI can distinguish failure paths;--version/-Vflag;python -m maithili_dslworks via new__main__.py.pyproject.toml(PEP 517/518) with pytest + coverage config co-located;setup.pyreduced to a legacy shim;python_requires>=3.9(was>=3.6); public API exports and__version__ = "0.3.0"inmaithili_dsl/__init__.py.tests/): functional, linter, security, CLI, smoke. 95% overall coverage; all critical modules ≥90%..github/workflows/ci.yml): 8-job matrix (Python 3.9/3.10/3.11/3.12 × Ubuntu/macOS) running lint + tests + coverage + smoke loop over every example on every push + PR.SECURITY.mdwith threat model and disclosure process,CHANGELOG.mdin Keep-a-Changelog format, PR template.Test Evidence
All four tiers green locally on macOS + Python 3.9:
Manual CLI smoke (exit codes):
Security Considerations
This PR lands the sandbox, so the threat-model check is part of the review:
MAITHILI_MODULESmaps 10 Maithili names to Python modules._ALLOWED_MODULES = set(MAITHILI_MODULES.values())is the whitelist. A test (test_allowed_modules_matches_maithili_modules) pins this invariant, and a parametrized test (test_dangerous_module_not_in_whitelist) fails ifsubprocess,socket,ctypes,pickle,shutil,urllib,http,ftplib, ortelnetlibever appear in the whitelist._validate_imports(static, pre-exec) catchesimport oseven when it didn't come from a Maithiliआयात ओएस._make_safe_import(runtime) catches any dynamic path that reaches__import__. Bypassing one still leaves the other.exec,eval,compile,open,breakpoint,__import__are not in_SAFE_BUILTIN_NAMES. End-to-end tests write.dmaifiles that call these names and assert they raise under the sandbox.Breaking Changes
python_maithili<0.3.cli.run_dmai_fileandcli.mainnow return an integer exit code instead ofNone. Callers relying on the previous implicit-Nonereturn should treat0as success.Consolidation plan
After this PR merges to
development, a release PRdevelopment→mainwill be opened to unfreezemain. After that, the staleorigin/fix/issue-with-linterbranch (already fully contained indevelopmentvia merged PR #1) will be deleted.Checklist
pytest -vruns green locally (142/142)python -m maithili_dsl examples/*.dmaismoke-tested all examplespython -m maithili_dsl --versionshows the expected versionmaithili_dsl/__init__.py)🤖 Generated with Claude Code