Skip to content

Commit ce3fb95

Browse files
authored
Cleanup Data Access (#73)
1 parent dd05d0e commit ce3fb95

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1251
-870
lines changed

backend/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
"@leoscope/openapi-response-validator": "^1.0.2",
3636
"@metlo/testing": "^0.0.3",
3737
"@types/async-retry": "^1.4.4",
38-
"@types/newman": "^5.3.0",
3938
"@types/ssh2": "^1.11.5",
4039
"async-retry": "^1.3.3",
4140
"aws-sdk": "^2.1189.0",
@@ -53,7 +52,6 @@
5352
"json-source-map": "^0.6.1",
5453
"lodash": "^4.17.21",
5554
"luxon": "^3.0.3",
56-
"memory-cache": "^0.2.0",
5755
"multer": "^1.4.5-lts.1",
5856
"node-schedule": "^2.1.0",
5957
"node-ssh": "^13.0.0",
@@ -79,7 +77,6 @@
7977
"@types/js-yaml": "^4.0.5",
8078
"@types/lodash": "^4.14.184",
8179
"@types/luxon": "^3.0.1",
82-
"@types/memory-cache": "^0.2.2",
8380
"@types/multer": "^1.4.7",
8481
"@types/node": "^18.6.1",
8582
"@types/node-schedule": "^2.1.0",

backend/src/analyze-traces.ts

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { v4 as uuidv4 } from "uuid"
22
import { AppDataSource } from "data-source"
3-
import { ApiTrace, ApiEndpoint, DataField, Alert } from "models"
3+
import { ApiTrace, ApiEndpoint, DataField, Alert, OpenApiSpec } from "models"
44
import { DataFieldService } from "services/data-field"
55
import { SpecService } from "services/spec"
66
import { AlertService } from "services/alert"
7-
import { DatabaseService } from "services/database"
87
import { RedisClient } from "utils/redis"
98
import { TRACES_QUEUE } from "~/constants"
109
import { QueryRunner } from "typeorm"
@@ -18,14 +17,20 @@ import {
1817
import { getPathTokens } from "@common/utils"
1918
import { AlertType } from "@common/enums"
2019
import { isGraphQlEndpoint } from "services/graphql"
20+
import { isQueryFailedError, retryTypeormTransaction } from "utils/db"
21+
import { MetloContext } from "types"
22+
import { DatabaseService } from "services/database"
23+
import { getEntityManager, getQB } from "services/database/utils"
2124

22-
const GET_ENDPOINT_QUERY = `
25+
const getEndpointQuery = (ctx: MetloContext) => `
2326
SELECT
2427
endpoint. *,
2528
CASE WHEN spec."isAutoGenerated" IS NULL THEN NULL ELSE json_build_object('isAutoGenerated', spec."isAutoGenerated") END as "openapiSpec"
2629
FROM
27-
"api_endpoint" endpoint
28-
LEFT JOIN "open_api_spec" spec ON endpoint."openapiSpecName" = spec.name
30+
${ApiEndpoint.getTableName(ctx)} endpoint
31+
LEFT JOIN ${OpenApiSpec.getTableName(
32+
ctx,
33+
)} spec ON endpoint."openapiSpecName" = spec.name
2934
WHERE
3035
$1 ~ "pathRegex"
3136
AND method = $2
@@ -39,7 +44,7 @@ LIMIT
3944
1
4045
`
4146

42-
const GET_DATA_FIELDS_QUERY = `
47+
const getDataFieldsQuery = (ctx: MetloContext) => `
4348
SELECT
4449
uuid,
4550
"dataClasses"::text[],
@@ -53,21 +58,27 @@ SELECT
5358
"dataPath",
5459
"apiEndpointUuid"
5560
FROM
56-
data_field
61+
${DataField.getTableName(ctx)} data_field
5762
WHERE
5863
"apiEndpointUuid" = $1
5964
`
6065

61-
const getQueuedApiTrace = async (): Promise<QueuedApiTrace> => {
66+
const getQueuedApiTrace = async (
67+
ctx: MetloContext,
68+
): Promise<QueuedApiTrace> => {
6269
try {
63-
const traceString = await RedisClient.popValueFromRedisList(TRACES_QUEUE)
70+
const traceString = await RedisClient.popValueFromRedisList(
71+
ctx,
72+
TRACES_QUEUE,
73+
)
6474
return JSON.parse(traceString)
6575
} catch (err) {
6676
return null
6777
}
6878
}
6979

7080
const analyze = async (
81+
ctx: MetloContext,
7182
trace: QueuedApiTrace,
7283
apiEndpoint: ApiEndpoint,
7384
queryRunner: QueryRunner,
@@ -76,11 +87,13 @@ const analyze = async (
7687
endpointUpdateDates(trace.createdAt, apiEndpoint)
7788
const dataFields = DataFieldService.findAllDataFields(trace, apiEndpoint)
7889
let alerts = await SpecService.findOpenApiSpecDiff(
90+
ctx,
7991
trace,
8092
apiEndpoint,
8193
queryRunner,
8294
)
8395
const sensitiveDataAlerts = await AlertService.createDataFieldAlerts(
96+
ctx,
8497
dataFields,
8598
apiEndpoint.uuid,
8699
apiEndpoint.path,
@@ -90,6 +103,7 @@ const analyze = async (
90103
alerts = alerts?.concat(sensitiveDataAlerts)
91104
if (newEndpoint) {
92105
const newEndpointAlert = await AlertService.createAlert(
106+
ctx,
93107
AlertType.NEW_ENDPOINT,
94108
apiEndpoint,
95109
)
@@ -99,18 +113,17 @@ const analyze = async (
99113
}
100114

101115
await queryRunner.startTransaction()
102-
await DatabaseService.retryTypeormTransaction(
116+
await retryTypeormTransaction(
103117
() =>
104-
queryRunner.manager.insert(ApiTrace, {
118+
getEntityManager(ctx, queryRunner).insert(ApiTrace, {
105119
...trace,
106120
apiEndpointUuid: apiEndpoint.uuid,
107121
}),
108122
5,
109123
)
110-
await DatabaseService.retryTypeormTransaction(
124+
await retryTypeormTransaction(
111125
() =>
112-
queryRunner.manager
113-
.createQueryBuilder()
126+
getQB(ctx, queryRunner)
114127
.insert()
115128
.into(DataField)
116129
.values(dataFields)
@@ -127,21 +140,19 @@ const analyze = async (
127140
.execute(),
128141
5,
129142
)
130-
await DatabaseService.retryTypeormTransaction(
143+
await retryTypeormTransaction(
131144
() =>
132-
queryRunner.manager
133-
.createQueryBuilder()
145+
getQB(ctx, queryRunner)
134146
.insert()
135147
.into(Alert)
136148
.values(alerts)
137149
.orIgnore()
138150
.execute(),
139151
5,
140152
)
141-
await DatabaseService.retryTypeormTransaction(
153+
await retryTypeormTransaction(
142154
() =>
143-
queryRunner.manager
144-
.createQueryBuilder()
155+
getQB(ctx, queryRunner)
145156
.update(ApiEndpoint)
146157
.set({
147158
firstDetected: apiEndpoint.firstDetected,
@@ -156,6 +167,7 @@ const analyze = async (
156167
}
157168

158169
const generateEndpoint = async (
170+
ctx: MetloContext,
159171
trace: QueuedApiTrace,
160172
queryRunner: QueryRunner,
161173
): Promise<void> => {
@@ -201,36 +213,35 @@ const generateEndpoint = async (
201213

202214
try {
203215
await queryRunner.startTransaction()
204-
await DatabaseService.retryTypeormTransaction(
216+
await retryTypeormTransaction(
205217
() =>
206-
queryRunner.manager
207-
.createQueryBuilder()
218+
getQB(ctx, queryRunner)
208219
.insert()
209220
.into(ApiEndpoint)
210221
.values(apiEndpoint)
211222
.execute(),
212223
5,
213224
)
214225
await queryRunner.commitTransaction()
215-
await analyze(trace, apiEndpoint, queryRunner, true)
226+
await analyze(ctx, trace, apiEndpoint, queryRunner, true)
216227
} catch (err) {
217228
if (queryRunner.isTransactionActive) {
218229
await queryRunner.rollbackTransaction()
219230
}
220-
if (DatabaseService.isQueryFailedError(err) && err.code === "23505") {
221-
const existingEndpoint = await queryRunner.manager.findOne(
222-
ApiEndpoint,
223-
{
224-
where: {
225-
path: trace.path,
226-
host: trace.host,
227-
method: trace.method,
228-
},
229-
relations: { dataFields: true },
231+
if (isQueryFailedError(err) && err.code === "23505") {
232+
const existingEndpoint = await getEntityManager(
233+
ctx,
234+
queryRunner,
235+
).findOne(ApiEndpoint, {
236+
where: {
237+
path: trace.path,
238+
host: trace.host,
239+
method: trace.method,
230240
},
231-
)
241+
relations: { dataFields: true },
242+
})
232243
if (existingEndpoint) {
233-
await analyze(trace, existingEndpoint, queryRunner)
244+
await analyze(ctx, trace, existingEndpoint, queryRunner)
234245
}
235246
} else {
236247
console.error(`Error generating new endpoint: ${err}`)
@@ -240,6 +251,8 @@ const generateEndpoint = async (
240251
}
241252

242253
const analyzeTraces = async (): Promise<void> => {
254+
const ctx: MetloContext = {}
255+
243256
const datasource = await AppDataSource.initialize()
244257
if (!datasource.isInitialized) {
245258
console.error("Couldn't initialize datasource...")
@@ -251,26 +264,26 @@ const analyzeTraces = async (): Promise<void> => {
251264
await queryRunner.connect()
252265
while (true) {
253266
try {
254-
const trace = await getQueuedApiTrace()
267+
const trace = await getQueuedApiTrace(ctx)
255268
if (trace) {
256269
trace.createdAt = new Date(trace.createdAt)
257270
const apiEndpoint: ApiEndpoint = (
258-
await queryRunner.query(GET_ENDPOINT_QUERY, [
271+
await queryRunner.query(getEndpointQuery(ctx), [
259272
trace.path,
260273
trace.method,
261274
trace.host,
262275
])
263276
)?.[0]
264277
if (apiEndpoint && !skipAutoGeneratedMatch(apiEndpoint, trace.path)) {
265-
const dataFields: DataField[] = await queryRunner.query(
266-
GET_DATA_FIELDS_QUERY,
278+
const dataFields: DataField[] = await DatabaseService.executeRawQuery(
279+
getDataFieldsQuery(ctx),
267280
[apiEndpoint.uuid],
268281
)
269282
apiEndpoint.dataFields = dataFields
270-
await analyze(trace, apiEndpoint, queryRunner)
283+
await analyze(ctx, trace, apiEndpoint, queryRunner)
271284
} else {
272285
if (trace.responseStatus !== 404 && trace.responseStatus !== 405) {
273-
await generateEndpoint(trace, queryRunner)
286+
await generateEndpoint(ctx, trace, queryRunner)
274287
}
275288
}
276289
}

backend/src/api/alert/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,30 @@ import { Request, Response } from "express"
22
import { AlertService } from "services/alert"
33
import { GetAlertParams, UpdateAlertParams } from "@common/types"
44
import ApiResponseHandler from "api-response-handler"
5+
import { MetloRequest } from "types"
56

67
export const getAlertsHandler = async (
7-
req: Request,
8+
req: MetloRequest,
89
res: Response,
910
): Promise<void> => {
1011
try {
1112
const alertParams: GetAlertParams = req.query
12-
const alerts = await AlertService.getAlerts(alertParams)
13+
const alerts = await AlertService.getAlerts(req.ctx, alertParams)
1314
await ApiResponseHandler.success(res, alerts)
1415
} catch (err) {
1516
await ApiResponseHandler.error(res, err)
1617
}
1718
}
1819

1920
export const updateAlertHandler = async (
20-
req: Request,
21+
req: MetloRequest,
2122
res: Response,
2223
): Promise<void> => {
2324
try {
2425
const { alertId } = req.params
2526
const updateAlertParams: UpdateAlertParams = req.body
2627
const updatedAlert = await AlertService.updateAlert(
28+
req.ctx,
2729
alertId,
2830
updateAlertParams,
2931
)

backend/src/api/alert/vulnerability.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { Request, Response } from "express"
1+
import { Response } from "express"
22
import { GetVulnerabilityAggParams } from "@common/types"
33
import ApiResponseHandler from "api-response-handler"
44
import { getVulnerabilityAgg } from "services/summary/vulnerabilities"
5+
import { MetloRequest } from "types"
56

67
export const getVulnerabilitySummaryHandler = async (
7-
req: Request,
8+
req: MetloRequest,
89
res: Response,
910
): Promise<void> => {
1011
try {
1112
const params: GetVulnerabilityAggParams = req.query
12-
const out = await getVulnerabilityAgg(params)
13+
const out = await getVulnerabilityAgg(req.ctx, params)
1314
await ApiResponseHandler.success(res, out)
1415
} catch (err) {
1516
await ApiResponseHandler.error(res, err)

0 commit comments

Comments
 (0)