원클릭으로
lua-audit
Audit Lua stdlib code for security, correctness, and sandbox safety. Use when reviewing or hardening Lua modules.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Audit Lua stdlib code for security, correctness, and sandbox safety. Use when reviewing or hardening Lua modules.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | lua-audit |
| description | Audit Lua stdlib code for security, correctness, and sandbox safety. Use when reviewing or hardening Lua modules. |
| user-invocable | true |
Perform comprehensive security, correctness, and quality audits on Hull Lua stdlib code.
Target: $ARGUMENTS (default: all stdlib/lua/hull/*.lua files)
/lua-audit # Audit all Lua stdlib modules
/lua-audit stdlib/lua/hull/template.lua # Audit a specific module
/lua-audit --fix # Audit and apply fixes
Hull Lua code runs inside a sandboxed Lua 5.4 interpreter:
io, os, loadfile, dofile, loadrequire(): resolves only from embedded stdlib registrydb, crypto, time, env, fs, http — accessed through hull globals, NOT Lua standard libs| Issue | Pattern to Find | Severity |
|---|---|---|
| Sandbox escape | Use of load(), loadstring(), dofile(), loadfile() | Critical |
| Unsafe eval | String-to-code conversion outside C bridge _compile() | Critical |
| Module smuggling | require() of non-hull modules | Critical |
| Global pollution | Writing to _G or global scope without local | High |
| Metatable abuse | setmetatable on shared objects that could affect other modules | High |
| Debug library | Use of debug.* (not loaded, but check for attempts) | Critical |
| rawget/rawset bypass | Circumventing metatables to access restricted data | Medium |
Hull-specific: The template engine's _template._compile() uses luaL_loadbuffer via C bridge — this is the ONLY allowed code compilation path. Verify no Lua-level code compilation exists.
| Issue | Pattern to Find | Severity |
|---|---|---|
| SQL injection | String concatenation in SQL queries | Critical |
| XSS via template | Unescaped user data in template output | High |
| Path traversal | Unsanitized paths passed to fs.* | High |
| Header injection | \r\n in HTTP header values | High |
| Command injection | User input in tool.spawn() arguments | Critical |
| HMAC timing attack | Non-constant-time string comparison of secrets/tokens | High |
| Regex DoS (ReDoS) | Unbounded string.find/string.gmatch on user input | Medium |
SQL safety check:
-- BAD: string concatenation
db.query("SELECT * FROM users WHERE id = " .. id)
-- GOOD: parameterized
db.query("SELECT * FROM users WHERE id = ?", {id})
Template safety check:
-- BAD: raw output of user data
template.render_string("{{{ user_input }}}", data)
-- GOOD: auto-escaped output
template.render_string("{{ user_input }}", data)
| Issue | Pattern to Find | Severity |
|---|---|---|
| Unchecked nil | Function return used without nil check | High |
| Silent failure | pcall that discards error message | Medium |
| Missing error propagation | Error condition not returned to caller | Medium |
Bare error() | Error without context message | Low |
| Unprotected external calls | db.query(), http.get() without error handling | Medium |
Patterns to check:
-- BAD: unchecked
local result = db.query("SELECT * FROM users WHERE id = ?", {id})
local name = result[1].name -- crashes if result is empty
-- GOOD: nil-safe
local result = db.query("SELECT * FROM users WHERE id = ?", {id})
if not result or #result == 0 then return nil, "not found" end
local name = result[1].name
| Issue | Pattern to Find | Severity |
|---|---|---|
| Missing type checks | Function params not validated with type() | Medium |
| Nil propagation | Nil values passed through chains without checks | Medium |
| Table/string confusion | # operator on potentially nil values | Medium |
| Number coercion | String used where number expected (or vice versa) | Low |
| Boolean truthiness | 0 and "" are truthy in Lua; {} is truthy | Medium |
Lua truthiness pitfalls:
-- BAD: 0 is truthy in Lua!
if count then -- true even when count == 0
-- GOOD:
if count and count > 0 then
-- BAD: empty table is truthy!
if items then -- true even when items == {}
-- GOOD:
if items and #items > 0 then
| Issue | Pattern to Find | Severity |
|---|---|---|
| Unbounded table growth | Tables that grow without limit (caches, logs) | High |
| Missing cache eviction | Caches without TTL or size limit | Medium |
| Closure leaks | Closures capturing large upvalues unnecessarily | Medium |
| String concatenation in loops | s = s .. chunk in loops (O(n^2)) | Medium |
| Large intermediate tables | Building tables that could exceed memory limit | Medium |
Performance patterns:
-- BAD: O(n^2) string building
local s = ""
for _, item in ipairs(items) do
s = s .. item -- copies entire string each iteration
end
-- GOOD: table.concat
local parts = {}
for _, item in ipairs(items) do
parts[#parts + 1] = item
end
local s = table.concat(parts)
| Issue | Pattern to Find | Severity |
|---|---|---|
| Hardcoded secrets | Literal strings used as HMAC/JWT secrets | Critical |
| Weak secrets | Short or predictable secret values | High |
| Timing attacks | == comparison on HMAC digests or tokens | High |
| Missing expiry | Tokens/sessions without TTL | Medium |
| Insecure defaults | Secure flag missing on cookies, HttpOnly not set | Medium |
| Nonce reuse | Same nonce/IV used for multiple encryptions | Critical |
Constant-time comparison:
-- BAD: early-exit comparison
if token == expected then
-- GOOD: use crypto.verify_password or HMAC-then-compare
-- Hull's jwt.verify and csrf.verify use constant-time internally
| Issue | What to Check | Severity |
|---|---|---|
| Missing API | Function exists in JS but not Lua (or vice versa) | Medium |
| Different behavior | Same function returns different types or formats | High |
| Naming mismatch | API names don't follow convention (Lua: snake_case, JS: camelCase) | Low |
| Different defaults | Default option values differ between runtimes | Medium |
| Error format | Different error message formats | Low |
| Issue | Pattern to Find | Severity |
|---|---|---|
| Code injection in codegen | User data interpolated into generated Lua source | Critical |
| Circular inheritance | {% extends %} chains without cycle detection | High |
| Unbounded recursion | Deeply nested includes without depth limit | High |
| Cache poisoning | Template cache key collision or manipulation | Medium |
| Filter bypass | Custom filter that returns unescaped HTML | Medium |
| Denial of service | Template that generates unbounded output | Medium |
| Pattern | Issue | Fix |
|---|---|---|
Unreachable code after return | Dead code | Remove |
| Unused local variables | Dead variable | Remove |
| Unused function parameters | Dead parameter | Prefix with _ |
| Commented-out code blocks | Dead code | Remove |
require of unused module | Dead import | Remove |
Empty if/else blocks | Dead branch | Remove |
When /lua-audit is invoked:
Locate Files
stdlib/lua/hull/*.lua # All Lua stdlib modules
examples/*/app.lua # Example apps (reference patterns)
tests/fixtures/*/app.lua # Test fixture apps
Scan for Critical Issues
load(, loadstring(, dofile(, loadfile( — sandbox escapes"SELECT.*".. or "INSERT.*"..{{{ in template strings — raw output of user data== comparison of secrets, tokens, hashes_G. or _G[ — global pollutionlocal on function-scoped variablesReview Each Module
Check Template Engine
& < > " 'Generate Report Format as markdown table with findings, severity, file:line, and suggested fix.
## Lua Audit Report: Hull
**Date:** YYYY-MM-DD
**Files Scanned:** N
**Issues Found:** N (Critical: N, High: N, Medium: N, Low: N)
### Critical Issues
| # | File:Line | Issue | Current Code | Suggested Fix |
|---|-----------|-------|--------------|---------------|
| C1 | stdlib/lua/hull/auth.lua:42 | SQL injection | `db.query("... " .. id)` | `db.query("... ?", {id})` |
### High Issues
...
### Medium Issues
...
### Low Issues
...
### Recommendations
1. ...
When --fix is specified:
make)make test && make e2e-templates)Auto-fixable Issues:
local declarations -> add local_s = s .. x in loops -> table.concat patternNOT Auto-fixable (require manual review):