1
0
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:
david
2012-08-14 16:36:25 +00:00
parent 16aa7a938d
commit 0c3e0fcc4d
16 changed files with 680 additions and 99 deletions

View File

@@ -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 >

View File

@@ -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

View File

@@ -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&ndash;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&ndash;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>&lt;elem key="username"&gt;foo&lt;/elem&gt;</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>&amp;#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="&#xa;hostname: slimer&#xa;users: &#xa; root&#xa; foo&#xa; 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>