mirror of
https://github.com/nmap/nmap.git
synced 2026-02-07 05:56:34 +00:00
Structured script output.
Scripts may now return a key–value table, or such a table in addition to a string. The table will be automatically formatted for normal output and will appear as a hierarchy of elements in XML output. Some history and discussion of this development can be found at https://secwiki.org/w/Nmap/Structured_Script_Output. This is a merge of r29484:29569 from /nmap-exp/david/xml-output.
This commit is contained in:
@@ -230,12 +230,22 @@
|
||||
|
||||
<!ELEMENT cpe (#PCDATA)>
|
||||
|
||||
<!ELEMENT script EMPTY >
|
||||
<!ELEMENT script (table|elem)* >
|
||||
<!ATTLIST script
|
||||
id CDATA #REQUIRED
|
||||
output CDATA #REQUIRED
|
||||
>
|
||||
|
||||
<!ELEMENT table (table|elem)* >
|
||||
<!ATTLIST table
|
||||
key CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT elem (#PCDATA)>
|
||||
<!ATTLIST elem
|
||||
key CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT os ( portused* , osmatch*, osfingerprint* ) >
|
||||
|
||||
<!ELEMENT portused EMPTY >
|
||||
|
||||
@@ -38,6 +38,11 @@ Additionally, you can use:
|
||||
-- | sample-script:
|
||||
-- | This is some output
|
||||
-- |_ Some more output
|
||||
-- @xmloutput
|
||||
-- <elem>This is some output</elem>
|
||||
-- <table>
|
||||
-- <elem>Some more output</elem>
|
||||
-- </table>
|
||||
--
|
||||
-- @args sample-script.arg1 Here, we document each argument, how it's used, and
|
||||
-- necessary, the default value.
|
||||
@@ -171,23 +176,45 @@ action = function( host, port )
|
||||
target.add('192.168.1.1')
|
||||
end
|
||||
|
||||
-- If your response is more complicated, you can build a table, potentially
|
||||
-- with subtables, and pass it to stdnse.format_output(). Each table can have
|
||||
-- a list of output values, numerically, which will be displayed in order.
|
||||
-- Additionally, they can have the 'name' key, which will be displayed at the
|
||||
-- top, and the 'warning' key, which will only be displayed if debugging is
|
||||
-- enabled. For more information and examples, see the documentation for
|
||||
-- stdnse.format_output().
|
||||
-- Construct a table representing what the script has to report.
|
||||
local output_tab = stdnse.output_table()
|
||||
output_tab.name1 = 'value1'
|
||||
output_tab.name2 = 'value2'
|
||||
output_tab.subtable = { 'sub1', 'sub2', 'sub3' }
|
||||
|
||||
-- Returning this table will produce output like this:
|
||||
-- | sample-script:
|
||||
-- | name1: value1
|
||||
-- | name2: value2
|
||||
-- | subtable:
|
||||
-- | sub1
|
||||
-- | sub2
|
||||
-- |_ sub3
|
||||
--
|
||||
-- If you need more control over output formatting, you can return a string in
|
||||
-- addition to the table. stdnse.format_output() is a formatting function used
|
||||
-- to make string output. Each table can have a list of output values,
|
||||
-- numerically, which will be displayed in order. Additionally, they can have
|
||||
-- the 'name' key, which will be displayed at the top, and the 'warning' key,
|
||||
-- which will only be displayed if debugging is enabled. For more information
|
||||
-- and examples, see the documentation for stdnse.format_output().
|
||||
--
|
||||
-- The following will display:
|
||||
-- | sample-script:
|
||||
-- | value1
|
||||
-- | value2
|
||||
-- | Name 1: value1
|
||||
-- | Name 2: value2
|
||||
-- | This is a subtable
|
||||
-- | subtable1
|
||||
-- |_ subtable2
|
||||
local response = {'value1', 'value2', {name="This is a subtable", 'subtable1', 'subtable2'}}
|
||||
return stdnse.format_output(true, response)
|
||||
-- | sub1
|
||||
-- | sub2
|
||||
-- |_ sub3
|
||||
|
||||
output_str = stdnse.format_output(true, {
|
||||
'Name 1: ' .. 'value1',
|
||||
'Name 2: ' .. 'value2',
|
||||
{ name='This is a subtable', 'sub1', 'sub2', 'sub3' }
|
||||
})
|
||||
|
||||
return output_tab, output_str
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1302,9 +1302,18 @@ NSE: Script Scanning completed.
|
||||
The action is the heart of an NSE script. It contains all of the
|
||||
instructions to be executed when the script's prerule, portrule, hostrule or postrule
|
||||
triggers. It is a Lua function which accepts the same arguments as the
|
||||
rule and can return either <literal>nil</literal> or a string. If a string is returned by a service script, the string and script's filename are printed in the Nmap port table output. A string returned by a host script is printed below the port table. No output is produced if the
|
||||
script returns <literal>nil</literal>. For an example of an NSE
|
||||
action refer to <xref linkend="nse-tutorial-action"/>.
|
||||
rule. The return value of the action value may be a table of
|
||||
name–value pairs, a string, or <code>nil</code>. For an example of
|
||||
an NSE action refer to <xref linkend="nse-tutorial-action"/>.
|
||||
</para>
|
||||
<para>
|
||||
If the output of the action is a table, it is automatically formatted in
|
||||
a structured fashion for inclusion in the normal (<option>-oN</option>)
|
||||
and XML (<option>-oX</option>) output formats. If a string, the text is
|
||||
displayed directly in normal output, and written as an XML attribute in
|
||||
XML output, No output is produced if the script returns
|
||||
<literal>nil</literal>. See <xref linkend="nse-structured-output"/> for
|
||||
details of how different return values are handled.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@@ -2166,6 +2175,140 @@ socket:close()
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="nse-structured-output">
|
||||
<title>Structured and Unstructured Output</title>
|
||||
<indexterm>structured script output</indexterm>
|
||||
|
||||
<para>
|
||||
NSE scripts should usually return a table representing their
|
||||
output, one that is nicely organized and has thoughtfully chosen
|
||||
keys. Such a table will be automatically formatted for screen
|
||||
output and will be stored as nested elements in XML output.
|
||||
Having XML output broken down logically into keys and values
|
||||
makes it easier for other tools to make use of script output.
|
||||
It is possible for a script to return only a string, but doing
|
||||
so is deprecated. In the past, scripts could only return a
|
||||
string, and their output was simply copied to the XML as a blob
|
||||
of text–this is now known as <quote>unstructured
|
||||
output</quote>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Suppose a script called <filename>user-list</filename> returns a
|
||||
table as shown in this code sample. The following paragraphs
|
||||
show how it appears in normal and XML output.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
local output = stdnse.output_table()
|
||||
output.hostname = "slimer"
|
||||
output.users = {}
|
||||
output.users[#output.users + 1] = "root"
|
||||
output.users[#output.users + 1] = "foo"
|
||||
output.users[#output.users + 1] = "bar"
|
||||
return output
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
A Lua table is converted to a string for normal output. The way
|
||||
this works is: each nested table gets a new level of
|
||||
indentation. Table entries with string keys are preceded by the
|
||||
key and a colon; entries with integer keys simply appear in
|
||||
order.
|
||||
Unlike normal Lua tables, which are unordered, a table that
|
||||
comes from <code>stdnse.output_table</code> will keep its keys in
|
||||
the order they were inserted.
|
||||
<xref linkend="nse-normal-structured-output"/> shows how the
|
||||
example table appears in normal output.
|
||||
</para>
|
||||
|
||||
<example id="nse-normal-structured-output">
|
||||
<title>Automatic formatting of NSE structured output</title>
|
||||
<screen>
|
||||
PORT STATE SERVICE
|
||||
1123/tcp open unknown
|
||||
| user-list:
|
||||
| hostname: slimer
|
||||
| users:
|
||||
| root
|
||||
| foo
|
||||
|_ bar
|
||||
</screen>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
The XML representation of a Lua table is constructed as follows.
|
||||
Nested table become <code>table</code> elements. Entries of
|
||||
tables that are not themselves tables become <code>elem</code>
|
||||
elements. Entries (whether <code>table</code> or
|
||||
<code>elem</code>) with string keys get a <code>key</code>
|
||||
attribute (e.g.
|
||||
<code><elem key="username">foo</elem></code>);
|
||||
entries with integer keys have no <code>key</code> element and
|
||||
their key is implicit in the order in which they appear.
|
||||
</para>
|
||||
<para>
|
||||
In addition to the above, whatever normal output the script
|
||||
produces (even if automatically generated) is copied to the
|
||||
<code>output</code> attribute of the <code>script</code>
|
||||
element. Newlines and other special characters will be encoded
|
||||
as XML character entities, for example <code>&#xa;</code>.
|
||||
<xref linkend="nse-xml-structured-output"/> shows how the example
|
||||
table appears in XML.
|
||||
</para>
|
||||
|
||||
<example id="nse-xml-structured-output">
|
||||
<title>NSE structured output in XML</title>
|
||||
<screen><![CDATA[<script id="t" output="
hostname: slimer
users: 
 root
 foo
 bar">
|
||||
<elem key="hostname">slimer</elem>
|
||||
<table key="users">
|
||||
<elem>root</elem>
|
||||
<elem>foo</elem>
|
||||
<elem>bar</elem>
|
||||
</table>
|
||||
</script>
|
||||
]]></screen>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
Some scripts need more control their normal output. This is the
|
||||
case, for example, with scripts that need to display complex
|
||||
tables. For complete control over the output, these scripts may
|
||||
do either of these things:
|
||||
<simplelist>
|
||||
<member>return a string as second return value, or</member>
|
||||
<member>set the <code>__tostring</code> metamethod on the
|
||||
returned table.</member>
|
||||
</simplelist>
|
||||
The resulting string will be used in normal output, and the
|
||||
table will be used in XML as usual. The formatted string may
|
||||
contain newline characters to appear as multiple lines.
|
||||
</para>
|
||||
<para>
|
||||
If the above code example were modified in this way to return a
|
||||
formatted string,
|
||||
<programlisting>
|
||||
local output = stdnse.output_table()
|
||||
output.hostname = "slimer"
|
||||
output.users = {}
|
||||
output.users[#output.users + 1] = "root"
|
||||
output.users[#output.users + 1] = "foo"
|
||||
output.users[#output.users + 1] = "bar"
|
||||
local output_str = string.format("hostname: %s\n", output.hostname)
|
||||
output_str = output_str .. "\n" .. stdnse.strjoin(", ", output.users)
|
||||
return output, output_str
|
||||
</programlisting>
|
||||
then the normal output would appear as follows:
|
||||
<screen>
|
||||
PORT STATE SERVICE
|
||||
1123/tcp open unknown
|
||||
| user-list:
|
||||
| hostname: slimer
|
||||
|_ users: root, foo, bar
|
||||
</screen>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="nse-exceptions">
|
||||
<title>Exception Handling</title>
|
||||
<indexterm><primary>exceptions in NSE</primary></indexterm>
|
||||
|
||||
Reference in New Issue
Block a user