|
1 | 1 | 'use strict' |
2 | 2 |
|
3 | | -const fs = require('fs') |
| 3 | +const { readFileSync } = require('node:fs') |
| 4 | + |
4 | 5 | const waf = require('./waf') |
5 | 6 | const { DIAGNOSTIC_KEYS } = require('./waf/diagnostics') |
6 | | -const { ACKNOWLEDGED, ERROR } = require('../remote_config/apply_states') |
7 | | -const Reporter = require('./reporter') |
8 | | - |
9 | 7 | const blocking = require('./blocking') |
10 | | - |
11 | | -const ASM_PRODUCTS = new Set(['ASM', 'ASM_DD', 'ASM_DATA']) |
| 8 | +const Reporter = require('./reporter') |
| 9 | +const { ASM_WAF_PRODUCTS_SET } = require('./rc-products') |
12 | 10 |
|
13 | 11 | /* |
14 | 12 | ASM Actions must be tracked in order to update the defaultBlockingActions in blocking. These actions are used |
15 | 13 | by blockRequest method exposed in the user blocking SDK (see packages/dd-trace/src/appsec/sdk/user_blocking.js) |
16 | 14 | */ |
17 | 15 | let appliedActions = new Map() |
18 | 16 |
|
| 17 | +/** |
| 18 | + * @typedef {object} AsmConfigFile |
| 19 | + * @property {Array<Record<string, unknown>>} [actions] |
| 20 | + */ |
| 21 | + |
| 22 | +/** |
| 23 | + * @typedef {import('./waf').WAFConfig & { rules?: string }} AppSecConfig |
| 24 | + */ |
| 25 | + |
| 26 | +/** |
| 27 | + * @param {AppSecConfig} config |
| 28 | + */ |
19 | 29 | function loadRules (config) { |
20 | 30 | const defaultRules = config.rules |
21 | | - ? JSON.parse(fs.readFileSync(config.rules)) |
| 31 | + ? JSON.parse(readFileSync(config.rules, 'utf8')) |
22 | 32 | : require('./recommended.json') |
23 | 33 |
|
24 | 34 | waf.init(defaultRules, config) |
25 | 35 |
|
26 | 36 | blocking.setDefaultBlockingActionParameters(defaultRules?.actions) |
27 | 37 | } |
28 | 38 |
|
29 | | -function updateWafFromRC ({ toUnapply, toApply, toModify }) { |
| 39 | +/** |
| 40 | + * Apply ASM remote-config updates to the WAF in a single batch. |
| 41 | + * |
| 42 | + * @param {import('../remote_config/manager').RcBatchUpdateTransaction} transaction |
| 43 | + */ |
| 44 | +function updateWafFromRC (transaction) { |
| 45 | + const { toUnapply, toApply, toModify } = transaction |
| 46 | + |
30 | 47 | const newActions = new SpyMap(appliedActions) |
31 | 48 |
|
32 | 49 | let wafUpdated = false |
33 | 50 | let wafUpdatedFailed = false |
34 | 51 |
|
35 | 52 | for (const item of toUnapply) { |
36 | | - if (!ASM_PRODUCTS.has(item.product)) continue |
| 53 | + if (!ASM_WAF_PRODUCTS_SET.has(item.product)) continue |
37 | 54 |
|
38 | 55 | try { |
39 | 56 | waf.removeConfig(item.path) |
40 | 57 |
|
41 | | - item.apply_state = ACKNOWLEDGED |
| 58 | + transaction.ack(item.path) |
42 | 59 | wafUpdated = true |
43 | 60 |
|
44 | 61 | // ASM actions |
45 | 62 | if (item.product === 'ASM') { |
46 | 63 | newActions.delete(item.id) |
47 | 64 | } |
48 | 65 | } catch (e) { |
49 | | - item.apply_state = ERROR |
50 | | - item.apply_error = e.toString() |
| 66 | + transaction.error(item.path, e) |
51 | 67 | wafUpdatedFailed = true |
52 | 68 | } |
53 | 69 | } |
54 | 70 |
|
55 | 71 | for (const item of [...toApply, ...toModify]) { |
56 | | - if (!ASM_PRODUCTS.has(item.product)) continue |
| 72 | + if (!ASM_WAF_PRODUCTS_SET.has(item.product)) continue |
57 | 73 |
|
58 | 74 | try { |
59 | 75 | waf.updateConfig(item.product, item.id, item.path, item.file) |
60 | 76 |
|
61 | | - item.apply_state = ACKNOWLEDGED |
| 77 | + transaction.ack(item.path) |
62 | 78 | wafUpdated = true |
63 | 79 |
|
64 | 80 | // ASM actions |
65 | | - if (item.product === 'ASM' && item.file?.actions?.length) { |
66 | | - newActions.set(item.id, item.file.actions) |
| 81 | + if (item.product === 'ASM') { |
| 82 | + const asmFile = /** @type {AsmConfigFile} */ (item.file) |
| 83 | + if (asmFile?.actions?.length) { |
| 84 | + newActions.set(item.id, asmFile.actions) |
| 85 | + } |
67 | 86 | } |
68 | 87 | } catch (e) { |
69 | | - item.apply_state = ERROR |
70 | | - item.apply_error = e instanceof waf.WafUpdateError |
71 | | - ? JSON.stringify(extractErrors(e.diagnosticErrors)) |
72 | | - : e.toString() |
| 88 | + const error = e instanceof waf.WafUpdateError ? JSON.stringify(extractErrors(e.diagnosticErrors)) : e |
| 89 | + transaction.error(item.path, error) |
73 | 90 | wafUpdatedFailed = true |
74 | 91 | } |
75 | 92 | } |
|
0 commit comments