1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-09 14:11:29 +00:00

Add json.lua, couchdb-databases.nse, and couchdb-stats.nse, all by

Martin Holst Swende.
This commit is contained in:
david
2010-02-28 21:25:01 +00:00
parent d9fd52c194
commit e89094261d
4 changed files with 777 additions and 0 deletions

View File

@@ -2,6 +2,11 @@
[NOT YET RELEASED] [NOT YET RELEASED]
o [NSE] Added the scripts couchdb-databases and couchdb-stats by
Martin Holst Swende, which list CouchDB databases and show access
statistics. These scripts use the new json.lua library, also by
Martin.
o Fixed the parsing of libdnet DLPI interface names that contain more o Fixed the parsing of libdnet DLPI interface names that contain more
than one string of digits. Joe Dietz reported that an interface with than one string of digits. Joe Dietz reported that an interface with
the name e1000g0 was causing the error message the name e1000g0 was causing the error message

513
nselib/json.lua Normal file
View File

@@ -0,0 +1,513 @@
--- Library methods for handling Json data. It handles
-- json encoding and decoding
--
-- There is a test-section at the bottom which shows some example
-- parsing. If you want to parse json, you can test it by pasting sample json
-- into the TESTS table and run the test() method
-- More info about Json at http://www.ietf.org/rfc/rfc4627.txt
-- !NOTE! Due to some differences between javascript and lua, there are some
-- conversion problems for null-values. Null-values in javascript are not equal to
-- nil values in lua. Nil is more corresponding to javascript 'undefined'.
-- As an example :
-- Executing the following javascript : var a= {b:null}; alert(a.b + " != " + a.c);
-- yields the string "null != undefined". Assigning a table a nil value in lua basically
-- removes it from the table, without leaving the key in place. I.e,
-- >a ={b=nil}
-- >print(a.b, a.c)
-- nil nil
-- !!!Therefore, javascript null values are represented by json.NULL.!!!
--
-- @author Martin Holst Swende
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
-- TODO: Unescape/escape unicode
-- Version 0.4
-- Created 01/25/2010 - v0.1 - created by Martin Holst Swende <martin@swende.se>
-- Heavily modified 02/22/2010 - v0.3. Rewrote the parser into an OO-form, to not have to handle
-- all kinds of state with parameters and return values.
-- Modified 02/27/2010 - v0.4 Added unicode handling (written by David Fifield). Renamed toJson
-- and fromJson intogenerate() and parse(), implemented more proper numeric parsing and added some more error checking.
module("json", package.seeall)
require("bit")
require("nsedebug")
--Some local shortcuts
local function dbg(str,...)
stdnse.print_debug("Json:"..str, unpack(arg))
end
local function d4(str,...)
if nmap.debugging() > 3 then dbg(str,unpack(arg)) end
end
local function d3(str,...)
if nmap.debugging() > 2 then dbg(str,unpack(arg)) end
end
--local dbg =stdnse.print_debug
local function dbg_err(str,...)
stdnse.print_debug("json-ERR:"..str, unpack(arg))
end
-- Javascript null representation, see explanation above
NULL = {}
local ESCAPE_TABLE={
['\\"']='"',
['\\/']= '/',
['\\b']=string.char(0x08),
['\\f']=string.char(0x0C),
['\\n']=string.char(0x0A),
['\\r']=string.char(0x0D),
['\\t']=string.char(0x09),
}
-- Escapes a string
--@param str the string
--@return a string where the special chars have been escaped
local function escape(str)
--This should be done first, so not in table
str = string.gsub(str, '\\\\', '\\')-- Escape \
for esc,char in pairs(ESCAPE_TABLE) do
str =str:gsub(char,esc)
end
--!TODO : Unicode escape
return ('"%s"'):format(str)
end
-- Creates json data from an object
--@param object a table containing data
--@return a string containing valid json
function generate(obj)
-- NULL-check must be performed before
-- checking type == table, since the NULL-object
-- is a table
if obj == NULL then
return "null"
elseif obj == false then
return "false"
elseif obj == true then
return "true"
elseif type(obj) == "number" then
return string.format("%g", obj)
elseif type(obj) == "string" then
return escape(obj)
elseif type(obj) == "table" then
local k, v, elems
elems = {}
if #obj > 0 then
-- Array
for _, v in ipairs(obj) do
elems[#elems + 1] = generate(v)
end
return "[" .. table.concat(elems, ", ") .. "]"
else
-- Object
for k, v in pairs(obj) do
elems[#elems + 1] = escape(k) .. ": " .. generate(v)
end
return "{" .. table.concat(elems, ", ") .. "}"
end
else
error("Unknown data type in generate")
end
end
local esc_chars =
{
['\\'] = '\\\\',
n =string.char(0x0A),
f=string.char(0x0C),
r=string.char(0x0D),
t=string.char(0x09),
b=string.char(0x08),
['"'] = '\"',
}
-- This is the parser, implemented in OO-form to deal with state better
Json = {}
-- Constructor
function Json:new(input)
local o = {}
setmetatable(o, self)
self.__index = self
o.input = input
o.pos = 1 -- Pos is where the NEXT letter will be read
return o
end
-- Gets next character and ups the position
--@return next character
function Json:next()
self.pos = self.pos+1
return self.input:sub(self.pos-1, self.pos-1)
end
-- Updates the position to next non whitespace position
function Json:eatWhiteSpace()
--Find next non-white char
local a,b = self.input:find("%S",self.pos)
if not a then
self:syntaxerror("Empty data")
return
end
self.pos = a
end
-- Jumps to a specified position
--@param position where to go
function Json:jumpTo(position)
self.pos = position
end
-- Returns next character, but without upping position
--@return next character
function Json:peek()
return self.input:sub(self.pos, self.pos)
end
--@return true if more input is in store
function Json:hasMore()
return self.input:len() >= self.pos
end
-- Checks that the following input is equal to a string
-- and updates position so next char will be after that string
-- If false, triggers a syntax error
--@param str the string to test
function Json:assertStr(str)
local content = self.input:sub(self.pos,self.pos+str:len()-1)
if(content == str) then-- All ok
-- Jump forward
self:jumpTo(self.pos+str:len())
return
end
self:syntaxerror(("Expected '%s' but got '%s'"):format( str, content))
end
-- Trigger a syntax error
function Json:syntaxerror(reason)
self.error = ("Syntax error near pos %d: %s input: %s"):format( self.pos, reason, self.input)
dbg(self.error)
end
-- Check if any errors has occurred
function Json:errors()
return self.error ~= nil
end
-- This is where the parsing begins.
--@return the parsed object or puts error messages in self.error
function Json:parseStart()
return self:parseValue(true)
end
-- Parses a value
--@param first if this is the first time the input is read,
-- the value must be array or object, otherwise a syntax error will
-- be triggered
--@return the parsed value
function Json:parseValue(first)
-- If first is set to true, this is the first
-- object received. Therefore, this must be
-- either an Object or Array ( first chars { or [ )
self:eatWhiteSpace()
local c = self:peek()
if(first and c ~= '{' and c ~= '[') then
self:syntaxerror(("Json must start with object or array (started with %s)"):format(c))
return
end
local value
if c == '{' then
value = self:parseObject()
elseif c == '[' then
value = self:parseArray()
elseif c == '"' then
value = self:parseString()
elseif c == 'n' then
self:assertStr("null")
value = NULL
elseif c == 't' then
self:assertStr("true")
value = true
elseif c == 'f' then
self:assertStr("false")
value = false
else -- numeric
-- number = [ minus ] int [ frac ] [ exp ]
local a,b =self.input:find("-?%d+%.?%d*[eE]?[+-]?%d*", self.pos)
if not a or not b then
self:syntaxerror("Error 1 parsing numeric value")
return
end
value = tonumber(self.input:sub(a,b))
if(value == nil) then
self:syntaxerror("Error 2 parsing numeric value")
return
end
self:jumpTo(b+1)
end
return value
end
-- Parses a json object {}
--@return the object (or triggers a syntax error)
function Json:parseObject()
local object = {}
local _= self:next() -- Eat {
while(self:hasMore() and not self:errors()) do
self:eatWhiteSpace()
local c = self:peek()
if(c == '}') then -- Empty object, probably
self:next() -- Eat it
return object
end
if(c ~= '"') then
self:syntaxerror(("Expected '\"', got '%s'"):format(c))
return
end
local key = self:parseString()
if self:errors() then
return
end
self:eatWhiteSpace()
c = self:next()
if(c ~= ':') then
self:syntaxerror("Expected ':' got "..c)
return
end
local value = self:parseValue()
if self:errors() then
return
end
object[key] = value
self:eatWhiteSpace()
c = self:next()
-- Valid now is , or }
if(c == '}') then
return object
end
if(c ~= ',') then
self:syntaxerror("Expected ',' or '}', got "..c)
return
end
end
end
-- Parses a json array [] or triggers a syntax error
--@return the array object
function Json:parseArray()
local array = {}
self:next()
while(self:hasMore() and not self:errors()) do
self:eatWhiteSpace()
if(self:peek() == ']') then -- Empty array, probably
self:next()
break
end
local value = self:parseValue()
if self:errors() then
return
end
table.insert(array, value)
self:eatWhiteSpace()
local c = self:next()
-- Valid now is , or ]
if(c == ']') then return array end
if(c ~= ',') then
self:syntaxerror(("Expected ',' but got '%s'"):format(c))
return
end
end
return array
end
-- Decode a Unicode escape, assuming that self.pos starts just after the
-- initial \u. May consume an additional escape in the case of a UTF-16
-- surrogate pair. See RFC 2781 for UTF-16.
function Json:parseUnicodeEscape()
local n, cp
local hex, lowhex
local s, e
s, e, hex = self.input:find("^(....)", self.pos)
if not hex then
self:syntaxerror(("EOF in Unicode escape \\u%s"):format(self.input:sub(self.pos)))
return
end
n = tonumber(hex, 16)
if not n then
self:syntaxerror(("Bad unicode escape \\u%s"):format(hex))
return
end
cp = n
self.pos = e + 1
if n < 0xD800 or n > 0xDFFF then
return cp
end
if n >= 0xDC00 and n <= 0xDFFF then
self:syntaxerror(("Not a Unicode character: U+%04X"):format(cp))
return
end
-- Beginning of a UTF-16 surrogate.
s, e, lowhex = self.input:find("^\\u(....)", self.pos)
if not lowhex then
self:syntaxerror(("Bad unicode escape \\u%s (missing low surrogate)"):format(hex))
return
end
n = tonumber(lowhex, 16)
if not n or not (n >= 0xDC00 and n <= 0xDFFF) then
self:syntaxerror(("Bad unicode escape \\u%s\\u%s (bad low surrogate)"):format(hex, lowhex))
return
end
self.pos = e + 1
cp = 0x10000 + bit.band(cp, 0x3FF) * 0x400 + bit.band(n, 0x3FF)
-- also remove last "
return cp
end
-- Encode a Unicode code point to UTF-8. See RFC 3629.
-- Does not check that cp is a real charaacter; that is, doesn't exclude the
-- surrogate range U+D800 - U+DFFF and a handful of others.
local function utf8_enc(cp)
local bytes = {}
local n, mask
if cp % 1.0 ~= 0.0 or cp < 0 then
-- Only defined for nonnegative integers.
return nil
elseif cp <= 0x7F then
-- Special case of one-byte encoding.
return string.char(cp)
elseif cp <= 0x7FF then
n = 2
mask = 0xC0
elseif cp <= 0xFFFF then
n = 3
mask = 0xE0
elseif cp <= 0x10FFFF then
n = 4
mask = 0xF0
else
return nil
end
while n > 1 do
bytes[n] = string.char(0x80 + bit.band(cp, 0x3F))
cp = bit.rshift(cp, 6)
n = n - 1
end
bytes[1] = string.char(mask + cp)
return table.concat(bytes)
end
-- Parses a json string
-- @return the string or triggers syntax error
function Json:parseString()
local val = ''
local c = self:next()
assert( c == '"')
while(self:hasMore()) do
local c = self:next()
if(c == '"') then -- end of string
break
elseif(c == '\\') then-- Escaped char
local d = self:next()
if esc_chars[d] ~= nil then
val = val .. esc_chars[d]
elseif d == 'u' then -- Unicode chars
local codepoint = self:parseUnicodeEscape()
if not codepoint then
return
end
val = val .. utf8_enc(codepoint)
else
self:syntaxerror(("Undefined escape character '%s'"):format(d))
return false
end
else -- Char
val = val .. c
end
end
return val
end
--- Parses json data into an object form
-- This is the method you probably want to use if you
-- use this library from a script.
--@param input : a json string
--@return status : true if ok, false if bad
--@return an object representing the json, or error message
function parse(data)
local parser = Json:new(data)
local result = parser:parseStart()
if(parser.error) then
return false, parser.error
end
return true, result
end
----------------------------------------------------------------------------------
-- Test-code for debugging purposes below
----------------------------------------------------------------------------------
local TESTS = {
'{"a":1}',
'{"a":true}',
'{"a": false}',
'{"a": null \r\n, \t "b" \f:"ehlo"}',
'{"a\\"a":"a\\"b\\"c\\"d"}',
'{"foo":"gaz\\"onk", "pi":3.14159,"hello":{ "wo":"rld"}}',
'{"a":1, "b":2}',
'{"foo":"gazonk", "pi":3.14159,"hello":{ "wo":"rl\\td"}}',
'[1,2,3,4,5,null,false,true,"\195\164\195\165\195\182\195\177","bar"]',
'[]',-- This will yield {} in toJson, since in lua there is only one basic datatype - and no difference when empty
'{}',
'', -- error
'null', -- error
'"abc"', -- error
'{a":1}', -- error
'{"a" bad :1}', -- error
'["a\\\\t"]', -- Should become Lua {"a\t"}
'[0.0.0]', -- error
'[-1]',
'[-1.123e-2]',
'[5e3]',
'[5e+3]',
'[5E-3]',
'[5.5e3]',
'["a\\\\"]', -- Should become Lua {"a\"}
'{"a}": 1}', -- Should become Lua {"a}" = 1}
'["key": "value"]', -- error
'["\\u0041"]', -- Should become Lua {"A"}
'["\\uD800"]', -- error
'["\\uD834\\uDD1EX"]', -- Should become Lua {"\240\157\132\158X"}
}
function test()
print("Tests running")
local i,v,res,status
for i,v in pairs(TESTS) do
print("----------------------------")
print(v)
status,res = parse(v)
if not status then print( res) end
if(status) then
print(generate(res))
else
print("Error:".. res)
end
end
end
test()

View File

@@ -0,0 +1,93 @@
description = [[
Gets database tables from a CouchDB database
For more info about the CouchDB HTTP Api, see
http://wiki.apache.org/couchdb/HTTP_database_API
]]
---
-- @usage
-- nmap -p 5984 --script "couchdb-databases.nse" <host>
-- @output
-- PORT STATE SERVICE REASON
-- 5984/tcp open unknown syn-ack
-- | couchdb-databases:
-- | 1 = test_suite_db
-- | 2 = test_suite_db_a
-- | 3 = test_suite_db/with_slashes
-- | 4 = moneyz
-- | 5 = creditcards
-- | 6 = test_suite_users
-- |_ 7 = test_suite_db_b
--@version 0.2
-- Created 01/12/2010 - v0.1 - created by Martin Holst Swende <martin@swende.se>
-- TODO : Authentication not implemented
author = "Martin Holst Swende"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}
require "shortport"
require "http"
require "json"
portrule = shortport.port_or_service({5984})
-- Some lazy shortcuts
local function dbg(str,...)
stdnse.print_debug("couchdb-get-tables:"..str, unpack(arg))
end
local DISCARD = {}
--- Removes uninteresting data from the table
-- uses the DISCARD table above to see what
-- keys should be omitted from the results
-- @param data a table containg data
--@return another table containing data, with some keys removed
local function queryResultToTable(data)
local result = {}
for k,v in pairs(data) do
dbg("(%s,%s)",k,tostring(v))
if DISCARD[k] ~= 1 then
if type(v) == 'table' then
table.insert(result,k)
table.insert(result,queryResultToTable(v))
else
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
end
end
end
return result
end
action = function(host, port)
local data, result, err
dbg("Requesting all databases")
data = http.get( host, port, '/_all_dbs' )
-- check that body was received
if not data.body or data.body == "" then
local msg = ("%s did not respond with any data."):format(host.targetname or host.ip )
dbg( msg )
return msg
end
-- The html body should look like this :
-- ["somedatabase", "anotherdatabase"]
local status, result = json.parse(data.body)
if not status then
dbg(result)
return result
end
-- Here we know it is a couchdb
port.version.name ='httpd'
port.version.product='Apache CouchDB'
nmap.set_port_version(host,port,'hardmatched')
-- We have a valid table in result containing the parsed json
-- now, get all the interesting bits
result = queryResultToTable(result)
return stdnse.format_output(true, result )
end

166
scripts/couchdb-stats.nse Normal file
View File

@@ -0,0 +1,166 @@
description = [[
Gets database statistics from a CouchDB database
For more info about the CouchDB HTTP Api, see
http://wiki.apache.org/couchdb/Runtime_Statistics
and
http://wiki.apache.org/couchdb/HTTP_database_API
]]
---
-- @usage
-- nmap -p 5984 --script "couchdb-stats.nse" <host>
-- @output
-- PORT STATE SERVICE REASON
-- 5984/tcp open httpd syn-ack
-- | couchdb-stats:
-- | httpd_request_methods
-- | GET (number of HTTP GET requests)
-- | current = 5
-- | count = 1617
-- | couchdb
-- | request_time (length of a request inside CouchDB without MochiWeb)
-- | current = 1
-- | count = 5
-- | httpd_status_codes
-- | 200 (number of HTTP 200 OK responses)
-- | current = 5
-- | count = 1617
-- | httpd
-- | requests (number of HTTP requests)
-- | current = 5
-- | count = 1617
-- |_ Authentication : NOT enabled ('admin party')
--@version 0.3
--
-- Created 01/20/2010 - v0.1 - created by Martin Holst Swende <martin@swende.se>
-- Modified 07/02/2010 - v0.2 - added test if auth is enabled, compacted output a bit (mhs)
author = "Martin Holst Swende"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}
require "shortport"
require "http"
require "json"
portrule = shortport.port_or_service({5984})
-- Some lazy shortcuts
local function dbg(str,...)
stdnse.print_debug("couchdb-stats:"..str, unpack(arg))
end
local DISCARD = {stddev=1,min=1,max=1, mean=1}
--- Removes uninteresting data from the table
-- uses the DISCARD table above to see what
-- keys should be omitted from the results
-- @param data a table containg data
--@return another table containing data, with some keys removed
local function queryResultToTable(data)
local result = {}
for k,v in pairs(data) do
dbg("(%s,%s)",k,tostring(v))
if DISCARD[k] ~= 1 then
if type(v) == 'table' then
if v["description"] ~= nil then
k = string.format("%s (%s)",tostring(k), tostring(v["description"]))
v["description"] = nil
end
table.insert(result,k)
table.insert(result,queryResultToTable(v))
else
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
end
end
end
return result
end
action = function(host, port)
local data, result, err
data = http.get( host, port, '/_stats' )
-- check that body was received
if not data.body or data.body == "" then
local msg = ("%s did not respond with any data."):format(hostrgetname or host.ip )
dbg( msg )
return msg
end
-- The html body should look like this :
--
--{"httpd_status_codes":{"200":{"current":10,"count":29894,"mean":0.0003345152873486337,"min":0,"max":1,"stddev":0.01828669972606202,"description":"number of HTTP 200 OK responses"},"500":{"current":1,"count":28429,"mean":0.00003517534911534013,"min":0,"max":1,"stddev":0.005930776661631644,"description":"number of HTTP 500 Internal Server Error responses"}},"httpd_request_methods":{"GET":{"current":12,"count":29894,"mean":0.00040141834481835866,"min":0,"max":2,"stddev":0.02163701147572207,"description":"number of HTTP GET requests"}},"httpd":{"requests":{"current":12,"count":29894,"mean":0.00040141834481835866,"min":0,"max":2,"stddev":0.02163701147572207,"description":"number of HTTP requests"}},"couchdb":{"request_time":{"current":23,"count":12,"mean":32.58333333333333,"min":1,"max":287,"stddev":77.76723638882608,"description":"length of a request inside CouchDB without MochiWeb"}}}
local status, result = json.parse(data.body)
if not status then
dbg(result)
return result
end
-- Here we know it is a couchdb
port.version.name ='httpd'
port.version.product='Apache CouchDB'
nmap.set_port_version(host,port,'hardmatched')
-- We have a valid table in result containing the parsed json
-- now, get all the interesting bits
result = queryResultToTable(result)
-- Additionally, we can check if authentication is used :
-- The following actions are restricted if auth is used
-- create db (PUT /database)
-- delete db (DELETE /database)
-- Creating a design document (PUT /database/_design/app)
-- Updating a design document (PUT /database/_design/app?rev=1-4E2)
-- Deleting a design document (DELETE /database/_design/app?rev=1-6A7)
-- Triggering compaction (POST /_compact)
-- Reading the task status list (GET /_active_tasks)
-- Restart the server (POST /_restart)
-- Read the active configuration (GET /_config)
-- Update the active configuration (PUT /_config)
data = http.get( host, port, '/_config' )
local status, authresult = json.parse(data.body)
-- If authorization is used, we should get back something like
-- {"error":"unauthorized","reason":"You are not a server admin."}
-- Otherwise, a *lot* of data, :
-- {"httpd_design_handlers":{"_info":"{couch_httpd_db, handle_design_info_req}",
-- "_list":"{couch_httpd_show, handle_view_list_req}","_show":"{couch_httpd_show, handle_doc_show_req}",
-- "_update":"{couch_httpd_show, handle_doc_update_req}","_view":"{couch_httpd_view, handle_view_req}"},
-- "httpd_global_handlers":{"/":"{couch_httpd_misc_handlers, handle_welcome_req, <<\"Welcome\">>}",
-- "_active_tasks":"{couch_httpd_misc_handlers, handle_task_status_req}",
-- "_all_dbs":"{couch_httpd_misc_handlers, handle_all_dbs_req}",
-- "_config":"{couch_httpd_misc_handlers, handle_config_req}",
-- "_log":"{couch_httpd_misc_handlers, handle_log_req}","_oauth":"{couch_httpd_oauth, handle_oauth_req}",
-- "_replicate":"{couch_httpd_misc_handlers, handle_replicate_req}","_restart":"{couch_httpd_misc_handlers, handle_restart_req}",
-- "_session":"{couch_httpd_auth, handle_session_req}","_sleep":"{couch_httpd_misc_handlers, handle_sleep_req}",
-- "_stats":"{couch_httpd_stats_handlers, handle_stats_req}","_user":"{couch_httpd_auth, handle_user_req}",
-- "_utils":"{couch_httpd_misc_handlers, handle_utils_dir_req, \"/usr/share/couchdb/www\"}",
-- "_uuids":"{couch_httpd_misc_handlers, handle_uuids_req}","favicon.ico":"{couch_httpd_misc_handlers, handle_favicon_req, \"/usr/share/couchdb/www\"}"},
-- "query_server_config":{"reduce_limit":"true"},"log":{"file":"/var/log/couchdb/0.10.0/couch.log","level":"info"},
-- "query_servers":{"javascript":"/usr/bin/couchjs /usr/share/couchdb/server/main.js"},
-- "daemons":{"batch_save":"{couch_batch_save_sup, start_link, []}","db_update_notifier":"{couch_db_update_notifier_sup, start_link, []}",
-- "external_manager":"{couch_external_manager, start_link, []}","httpd":"{couch_httpd, start_link, []}",
-- "query_servers":"{couch_query_servers, start_link, []}","stats_aggregator":"{couch_stats_aggregator, start, []}",
-- "stats_collector":"{couch_stats_collector, start, []}","view_manager":"{couch_view, start_link, []}"},
-- "httpd":{"WWW-Authenticate":"Basic realm=\"administrator\"","authentication_handlers":"{couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, default_authentication_handler}",
-- "bind_address":"127.0.0.1","default_handler":"{couch_httpd_db, handle_request}","port":"5984"},"httpd_db_handlers":{"_changes":"{couch_httpd_db, handle_changes_req}",
-- "_compact":"{couch_httpd_db, handle_compact_req}","_design":"{couch_httpd_db, handle_design_req}","_temp_view":"{couch_httpd_view, handle_temp_view_req}",
-- "_view":"{couch_httpd_view, handle_db_view_req}","_view_cleanup":"{couch_httpd_db, handle_view_cleanup_req}"},
-- "couch_httpd_auth":{"authentication_db":"users","require_valid_user":"false","secret":"replace this with a real secret in your local.ini file"},
-- "couchdb":{"batch_save_interval":"1000","batch_save_size":"1000","database_dir":"/var/lib/couchdb/0.10.0","delayed_commits":"true",
-- "max_attachment_chunk_size":"4294967296","max_dbs_open":"100","max_document_size":"4294967296",
-- "os_process_timeout":"5000","util_driver_dir":"/usr/lib/couchdb/erlang/lib/couch-0.10.0/priv/lib","view_index_dir":"/var/lib/couchdb/0.10.0"}}
local auth = "Authentication : %s"
local authEnabled = "unknown"
if(status) then
if(authresult["error"] == "unauthorized") then authEnabled = "enabled"
elseif (authresult["httpd_design_handlers"] ~= nil) then authEnabled = "NOT enabled ('admin party')"
end
end
table.insert(result, auth:format(authEnabled))
return stdnse.format_output(true, result )
end