Skip to content

Commit c2d983c

Browse files
authored
Merge pull request #718 from dappnode/dapplion/validate-https-response
Validate HTTPs API response type
2 parents 3c03dd3 + 15ccf08 commit c2d983c

File tree

3 files changed

+87
-67
lines changed

3 files changed

+87
-67
lines changed

packages/dappmanager/src/modules/https-portal/apiClient.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import fetch from "node-fetch";
2+
import Ajv from "ajv";
23
import querystring from "querystring";
34
import { urlJoin } from "../../utils/url";
45

6+
const ajv = new Ajv({ allErrors: true });
7+
58
export interface HttpPortalEntry {
69
/**
710
* The public subdomain
@@ -16,6 +19,18 @@ export interface HttpPortalEntry {
1619
toHost: string;
1720
}
1821

22+
export const httpsPortalResponseSchema = {
23+
type: "array",
24+
items: {
25+
type: "object",
26+
required: ["from", "to"],
27+
properties: {
28+
from: { type: "string" },
29+
to: { type: "string" }
30+
}
31+
}
32+
};
33+
1934
export class HttpsPortalApiClient {
2035
baseUrl: string;
2136

@@ -88,7 +103,13 @@ export class HttpsPortalApiClient {
88103
}
89104

90105
try {
91-
return JSON.parse(body);
106+
const response = JSON.parse(body);
107+
108+
if (!ajv.validate(httpsPortalResponseSchema, response)) {
109+
throw Error(`Invalid response: ${JSON.stringify(ajv.errors, null, 2)}`);
110+
}
111+
112+
return response;
92113
} catch (e) {
93114
throw Error(`Error parsing JSON ${e.message}\n${body}`);
94115
}

packages/dappmanager/src/modules/portsTable/performPortsScan.ts

Lines changed: 5 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import params from "../../params";
33
import Ajv from "ajv";
44
import { PackagePort } from "../../common";
55
import { logs } from "../../logs";
6+
import { checkPortsResponseSchema } from "./schema";
67

78
interface PortScanResponse {
89
tcpPorts: PortScanResult[];
@@ -60,71 +61,9 @@ export async function performPortsScan({
6061
* Check if the response from the scan ports API
6162
* is well formatted
6263
*/
63-
function sanitizeApiResponse(response: any): PortScanResponse {
64-
//If the keyword value is an object, then for the data array to be valid
65-
// each item of the array should be valid according to the schema in this value.
66-
// In this case the additionalItems keyword is ignored.
67-
const schema = {
68-
type: "object",
69-
maxProperties: 2,
70-
additionalProperties: false,
71-
properties: {
72-
tcpPorts: {
73-
uniqueItems: true,
74-
type: "array",
75-
items: {
76-
type: "object",
77-
maxProperties: 3,
78-
minProperties: 2,
79-
additionalProperties: false,
80-
required: ["port", "status"],
81-
properties: {
82-
port: {
83-
type: "integer",
84-
minimum: 0,
85-
maximum: 65535
86-
},
87-
status: {
88-
type: "string",
89-
pattern: "^(open|closed|error|unknown)$"
90-
},
91-
message: {
92-
type: "string"
93-
}
94-
}
95-
}
96-
},
97-
udpPorts: {
98-
uniqueItems: true,
99-
type: "array",
100-
items: {
101-
type: "object",
102-
maxProperties: 3,
103-
minProperties: 2,
104-
additionalProperties: false,
105-
required: ["port", "status"],
106-
properties: {
107-
port: {
108-
type: "integer",
109-
minimum: 0,
110-
maximum: 65535
111-
},
112-
status: {
113-
type: "string",
114-
pattern: "^(open|closed|error|unknown)$"
115-
},
116-
message: {
117-
type: "string"
118-
}
119-
}
120-
}
121-
}
122-
}
123-
};
124-
125-
if (ajv.validate(schema, response)) {
126-
const responseSanitized = response as PortScanResponse;
127-
return responseSanitized;
64+
function sanitizeApiResponse(response: unknown): PortScanResponse {
65+
if (!ajv.validate(checkPortsResponseSchema, response)) {
66+
throw Error(`Invalid response: ${JSON.stringify(ajv.errors, null, 2)}`);
12867
}
129-
throw Error("Error: API response not validated by the schema");
68+
return response as PortScanResponse;
13069
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// If the keyword value is an object, then for the data array to be valid
2+
// each item of the array should be valid according to the schema in this value.
3+
// In this case the additionalItems keyword is ignored.
4+
export const checkPortsResponseSchema = {
5+
type: "object",
6+
maxProperties: 2,
7+
additionalProperties: false,
8+
properties: {
9+
tcpPorts: {
10+
uniqueItems: true,
11+
type: "array",
12+
items: {
13+
type: "object",
14+
maxProperties: 3,
15+
minProperties: 2,
16+
additionalProperties: false,
17+
required: ["port", "status"],
18+
properties: {
19+
port: {
20+
type: "integer",
21+
minimum: 0,
22+
maximum: 65535
23+
},
24+
status: {
25+
type: "string",
26+
pattern: "^(open|closed|error|unknown)$"
27+
},
28+
message: {
29+
type: "string"
30+
}
31+
}
32+
}
33+
},
34+
udpPorts: {
35+
uniqueItems: true,
36+
type: "array",
37+
items: {
38+
type: "object",
39+
maxProperties: 3,
40+
minProperties: 2,
41+
additionalProperties: false,
42+
required: ["port", "status"],
43+
properties: {
44+
port: {
45+
type: "integer",
46+
minimum: 0,
47+
maximum: 65535
48+
},
49+
status: {
50+
type: "string",
51+
pattern: "^(open|closed|error|unknown)$"
52+
},
53+
message: {
54+
type: "string"
55+
}
56+
}
57+
}
58+
}
59+
}
60+
};

0 commit comments

Comments
 (0)