-
Notifications
You must be signed in to change notification settings - Fork 370
/
nmap-nse-linter.sh
196 lines (184 loc) · 5.65 KB
/
nmap-nse-linter.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/bin/bash
# Lua 5.3 - Nmap NSE linter
if [ -n "$LUA" ]; then
if ! "$LUA" -v 2>&1 | grep 5.3 > /dev/null; then
echo Lua 5.3 required. Skipping Lua checks.
fi
elif ! lua -v 2>&1 | grep 5.3 > /dev/null; then
echo Lua 5.3 required. Skipping Lua checks.
else
LUA=$(which lua)
fi
if [ -n "$LUAC" ]; then
if ! "$LUAC" -v 2>&1 | grep 5.3 > /dev/null; then
echo Luac 5.3 required. Skipping Lua checks.
fi
elif ! luac -v 2>&1 | grep 5.3 > /dev/null; then
echo Luac 5.3 required. Skipping Lua checks.
else
LUAC=$(which luac)
fi
export LUA
export LUAC
if [ -n "$LUA" -a -n "$LUAC" ]; then
TOPDIR="/usr/local/share/nmap"
NSE_LIBRARIES=""
for lib in "$TOPDIR"/nselib/*.luadoc; do
NSE_LIBRARIES="$NSE_LIBRARIES$(basename "$lib" .luadoc)"$'\n'
done
for lib in "$TOPDIR"/nselib/*.lua; do
NSE_LIBRARIES="$NSE_LIBRARIES$(basename "$lib" .lua)"$'\n'
done
export NSE_LIBRARIES
for file in $(find "$@" -name '*.nse' -o -name '*.lua'); do
OUTPUT="output.tmp"
echo "Checking $file ..."
"$LUAC" -l -p "$file" > /dev/null 2> "$OUTPUT"
if [ -s "$OUTPUT" ]; then
sed "s|^$LUAC: ||" < "$OUTPUT" > /dev/stderr
rm -f "$OUTPUT"
exit 1
fi
rm -f "$OUTPUT"
"$LUA" - "$file" <<EOF
local NSE_LIBRARIES = "\\n"..os.getenv("NSE_LIBRARIES").."\\n"; -- add delimiters
local LUA_LIBRARIES = {
string = true,
debug = true,
package = true,
_G = true,
io = true,
os = true,
table = true,
math = true,
coroutine = true,
bit32 = true,
};
IGNORE = {
_M = true,
_NAME = true,
_PACKAGE = true,
};
local file = arg[1];
arg = nil; -- clear from global namespace
if not file or not io.open(file, "r") then
io.stdout:write("no file argument specified.\\n");
os.exit(1);
end
local command = os.getenv "LUAC" .. " -l -p " .. file .. "\\n";
local required = {};
local get_globals = {};
local set_globals = {};
local main_set = {};
if ("$file"):match ".nse\$" then
main_set.SCRIPT_NAME = true;
main_set.SCRIPT_PATH = true;
main_set.SCRIPT_TYPE = true;
end
local main = true;
local first_loc = 1;
local registers = {};
local required_fields = {description = false; author = false; license = false; categories = false};
local exit_status = 0;
for line in assert(io.popen(command)):lines() do
if main and line:find "^function" then
main = false;
end
-- sometimes we see this:
-- 428 [4680] LOADK 12 -258 ; "get_pad"
-- 429 [4683] CLOSURE 13 67 ; 0xcf41e0
-- 430 [4680] SETTABUP 0 12 13 ; _ENV
-- We must sadly save what constants are loaded into registers (simply)
-- to determine what the key is for SETTABUP. There is no need to clear
-- the registers.
local r, constant = line:match("^%s%d+%s%[%d+%]%sLOADK%s+(%d+).-; \\"([%w_]+)\\"");
if constant then
registers[r] = constant;
end
local get_n, get_global = line:match("^%s%d+%s%[(%d+)%]%sGETTABUP.-; _ENV \\"([%w_]+)\\"");
if not get_n then
local r;
get_n, r = line:match("^%s%d+%s%[(%d+)%]%sGETTABUP%s+%d+%s+%d+%s+(%d+).-; _ENV");
if r then
get_global = registers[r];
if not get_global then get_n = nil end
end
end
local set_n, set_global = line:match("^%s%d+%s%[(%d+)%]%sSETTABUP.-; _ENV \\"([%w_]+)\\"");
if not set_n then
local r;
set_n, r = line:match("^%s%d+%s%[(%d+)%]%sSETTABUP%s+%d+%s+(%d+).-; _ENV");
if r then
set_global = registers[r];
if not set_global then set_n = nil end
end
end
if get_n then
if IGNORE[get_global] then
-- ignore it
elseif NSE_LIBRARIES:find("\\n"..get_global.."\\n", 1, true) or LUA_LIBRARIES[get_global] then
-- found global library, needs to be required
--io.stdout:write("found global library ", get_global);
if not required[get_global] then
required[get_global] = get_n;
required[#required+1] = get_global;
end
elseif _G[get_global] then
-- found global Lua function, this is okay
--io.stdout:write("found global ", get_global);
else
-- found global which may be "set", so we wait to report it
if not get_globals[get_global] then
get_globals[get_global] = get_n;
end
--io.stdout:write("found other global ", get_global);
end
elseif set_n then
if main then
-- Setting globals in main is okay.
main_set[set_global] = true;
--io.stdout:write("found main set global ", set_global);
else
-- Add to list of globals set which may be errors.
if not set_globals[set_global] then
set_globals[set_global] = set_n;
end
--io.stdout:write("found set global ", set_global);
end
end
::next_line::
end
-- go through list of libraries that need required, emit a patch
if next(required) then
exit_status = 1
table.sort(required);
for i, global in ipairs(required) do
local line = required[global];
io.stdout:write("$file:", line, ": Found unrequired NSE library \\"", global, "\\".\\n");
end
end
-- go through list of get_globals, if not in main_set, then error
for global, line in pairs(get_globals) do
if main_set[global] then
-- user is getting a global variable which we consider okay
-- since this global was set previously in the main function
else
exit_status = 1
io.stdout:write("$file:", line, ": Found bad indexed global \\"", global, "\\".\\n");
end
end
-- go through list of set_globals, if not in main_set, then error
for global, line in pairs(set_globals) do
if main_set[global] then
-- user is setting a global variable which we consider okay
-- since this global was set previously in the main function
else
exit_status = 1
io.stdout:write("$file:", line, ": Found bad set global \\"", global, "\\".\\n");
end
end
os.exit(exit_status)
EOF
done
fi
exit 0