355 lines
7.6 KiB
TypeScript
355 lines
7.6 KiB
TypeScript
namespace _heimdall.check_kinds.http_request
|
|
{
|
|
|
|
/**
|
|
*/
|
|
function parameters_schema(
|
|
) : _heimdall.helpers.json_schema.type_schema
|
|
{
|
|
return {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"request": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"target": {
|
|
"description": "URL",
|
|
"type": "string"
|
|
},
|
|
"method": {
|
|
"type": "string",
|
|
"enum": [
|
|
"GET",
|
|
"POST"
|
|
],
|
|
"default": "GET"
|
|
}
|
|
},
|
|
"required": [
|
|
"target"
|
|
]
|
|
},
|
|
"timeout": {
|
|
"description": "maximum allowed execution time in seconds",
|
|
"type": "number",
|
|
"default": 5.0
|
|
},
|
|
"follow_redirects": {
|
|
"description": "whether redirect instructions in responses shall be followend instead of being exposed as result",
|
|
"type": "boolean",
|
|
"default": false
|
|
},
|
|
"response": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"status_code": {
|
|
"description": "checks whether the response status code is this",
|
|
"type": ["null", "integer"],
|
|
"default": 200
|
|
},
|
|
"headers": {
|
|
"description": "conjunctively checks header key-value pairs",
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"description": "header value",
|
|
"type": "string"
|
|
},
|
|
"properties": {
|
|
},
|
|
"required": [
|
|
],
|
|
"default": {}
|
|
},
|
|
"body_part": {
|
|
"description": "checks whether the response body contains this string",
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
]
|
|
},
|
|
"critical": {
|
|
"description": "whether a violation of this check shall be leveled as critical instead of concerning",
|
|
"type": "boolean",
|
|
"default": true
|
|
},
|
|
"strict": {
|
|
"deprecated": true,
|
|
"description": "alias for 'critical'",
|
|
"type": "boolean",
|
|
"default": true
|
|
},
|
|
},
|
|
"required": [
|
|
"request",
|
|
]
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
function normalize_order_node(
|
|
node : any
|
|
) : any
|
|
{
|
|
const version : string = (
|
|
(! ("critical" in node))
|
|
? "v1"
|
|
: "v2"
|
|
);
|
|
|
|
switch (version) {
|
|
default: {
|
|
throw (new Error("unhandled version"));
|
|
break;
|
|
}
|
|
case "v1": {
|
|
const node_ = lib_plankton.object.patched(
|
|
{
|
|
"request": {
|
|
"method": "GET"
|
|
},
|
|
"timeout": 5.0,
|
|
"follow_redirects": false,
|
|
"response": {
|
|
"status_code": 200
|
|
},
|
|
"strict": true,
|
|
},
|
|
node,
|
|
true
|
|
);
|
|
const allowed_methods : Array<string> = ["GET", "POST"];
|
|
if (! allowed_methods.includes(node_["request"]["method"])) {
|
|
throw (new Error("invalid HTTP request method: " + node_["request"]["method"]));
|
|
}
|
|
else {
|
|
return {
|
|
"request": node_["request"],
|
|
"timeout": node_["timeout"],
|
|
"follow_redirects": node_["follow_redirects"],
|
|
"response": node_["response"],
|
|
"critical": node_["strict"],
|
|
};
|
|
}
|
|
break;
|
|
}
|
|
case "v2": {
|
|
const node_ = lib_plankton.object.patched(
|
|
{
|
|
"request": {
|
|
"method": "GET"
|
|
},
|
|
"timeout": 5.0,
|
|
"follow_redirects": false,
|
|
"response": {
|
|
"status_code": 200
|
|
},
|
|
"critical": true,
|
|
},
|
|
node,
|
|
true
|
|
);
|
|
const allowed_methods : Array<string> = ["GET", "POST"];
|
|
if (! allowed_methods.includes(node_["request"]["method"])) {
|
|
throw (new Error("invalid HTTP request method: " + node_["request"]["method"]));
|
|
}
|
|
else {
|
|
return node_;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
async function run(
|
|
parameters
|
|
) : Promise<_heimdall.type_result>
|
|
{
|
|
let error : (null | Error);
|
|
const http_request : lib_plankton.http.type_request = {
|
|
"host": parameters["request"]["target"],
|
|
"query": "",
|
|
"method": {
|
|
"GET": lib_plankton.http.enum_method.get,
|
|
"POST": lib_plankton.http.enum_method.post,
|
|
}[parameters["request"]["method"]],
|
|
"headers": {},
|
|
"body": "",
|
|
};
|
|
let http_response : (null | lib_plankton.http.type_response);
|
|
try {
|
|
http_response = await lib_plankton.http.call(
|
|
http_request,
|
|
{
|
|
"timeout": parameters["timeout"],
|
|
"follow_redirects": parameters["follow_redirects"],
|
|
}
|
|
);
|
|
error = null;
|
|
}
|
|
catch (error_) {
|
|
http_response = null;
|
|
error = error_;
|
|
}
|
|
if (http_response === null) {
|
|
return {
|
|
"condition": (
|
|
parameters["strict"]
|
|
? _heimdall.enum_condition.critical
|
|
: _heimdall.enum_condition.concerning
|
|
),
|
|
"info": {
|
|
"request": parameters["request"],
|
|
"faults": [
|
|
lib_plankton.translate.get("checks.http_request.request_failed"),
|
|
],
|
|
},
|
|
};
|
|
}
|
|
else {
|
|
let faults : Array<string> = [];
|
|
// status code
|
|
{
|
|
if (
|
|
(! ("status_code" in parameters["response"]))
|
|
||
|
|
(parameters["response"]["status_code"] === null)
|
|
) {
|
|
// do nothing
|
|
}
|
|
else {
|
|
const status_code_expected : int = (parameters["response"]["status_code"] as int);
|
|
if (http_response.statuscode === status_code_expected) {
|
|
faults.push(
|
|
lib_plankton.translate.get(
|
|
"checks.http_request.status_code_mismatch",
|
|
{
|
|
"status_code_actual": http_response.statuscode.toFixed(0),
|
|
"status_code_expected": status_code_expected.toFixed(0),
|
|
}
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
// headers
|
|
{
|
|
if (
|
|
(! ("headers" in parameters["response"]))
|
|
||
|
|
(parameters["response"]["headers"] === null)
|
|
) {
|
|
// do nothing
|
|
}
|
|
else {
|
|
const headers_expected : Record<string, string> = (parameters["response"]["headers"] as Record<string, string>);
|
|
Object.entries(headers_expected).forEach(
|
|
([header_key, header_value]) => {
|
|
if (! (header_key in http_response.headers)) {
|
|
faults.push(
|
|
lib_plankton.translate.get(
|
|
"checks.http_request.header_missing",
|
|
{
|
|
"key": header_key,
|
|
"value_expected": header_value,
|
|
}
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
if (! (http_response.headers[header_key] === header_value)) {
|
|
faults.push(
|
|
lib_plankton.translate.get(
|
|
"checks.http_request.header_value_mismatch",
|
|
{
|
|
"key": header_key,
|
|
"value_actual": http_response.headers[header_key],
|
|
"value_expected": header_value,
|
|
}
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
// body
|
|
{
|
|
if (
|
|
(! ("body_part" in parameters["response"]))
|
|
||
|
|
(parameters["response"]["body_part"] === null)
|
|
) {
|
|
// do nothing
|
|
}
|
|
else {
|
|
const body_part : string = (parameters["response"]["body_part"] as string);
|
|
if (! http_response.body.includes(body_part)) {
|
|
faults.push(
|
|
lib_plankton.translate.get(
|
|
"checks.http_request.body_misses_part",
|
|
{
|
|
"part": body_part,
|
|
}
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
"condition": (
|
|
(faults.length <= 0)
|
|
? _heimdall.enum_condition.ok
|
|
: (
|
|
parameters["critical"]
|
|
? _heimdall.enum_condition.critical
|
|
: _heimdall.enum_condition.concerning
|
|
)
|
|
),
|
|
"info": {
|
|
"request": parameters["request"],
|
|
"response": {
|
|
"status_code": http_response.statuscode,
|
|
"headers": http_response.headers,
|
|
// "body": http_response.body,
|
|
},
|
|
"faults": faults,
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
export function check_kind_implementation(
|
|
) : type_check_kind
|
|
{
|
|
return {
|
|
"parameters_schema": parameters_schema,
|
|
"normalize_order_node": normalize_order_node,
|
|
"run": run,
|
|
};
|
|
}
|
|
|
|
}
|