feat(api): enhance host firewall handling and validation
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m56s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

- Introduced a new `sanitizeHostFirewall` function to filter and validate host firewall rules and listeners, ensuring only valid entries are processed.
- Updated the `apply-report` endpoint to prevent overwriting existing host firewall snapshots with empty payloads, improving data integrity.
- Enhanced the `applyReportHostFirewallSchema` to define the expected structure for host firewall data, allowing for better validation and error handling.
- Added tests to verify the behavior of the new sanitization logic and the preservation of existing snapshots, ensuring robustness in the API's handling of firewall data.

These changes improve the reliability and accuracy of host firewall data management within the API, enhancing overall monitoring capabilities.
This commit is contained in:
Denozordec
2026-08-15 13:27:49 +07:00
parent bfaed511bd
commit ef2a333430
4 changed files with 239 additions and 78 deletions
+107
View File
@@ -278,6 +278,113 @@ describe('port ACL + host firewall snapshot', () => {
expect(body.listeners[0]?.port).toBe(22)
})
it('does not wipe host_firewall snapshot with empty apply-report payload', async () => {
const app = await appPromise
await app.ready()
const { agentId, token } = await enrollApprovedLinux(
app,
'host-fw-empty',
'evofw_host_fw_empty_token_ab',
)
const filled = await app.inject({
method: 'POST',
url: '/v1/agent/apply-report',
headers: {
authorization: `Bearer ${token}`,
'content-type': 'application/json',
},
payload: {
status: 'ok',
host_firewall: {
rules: [
{
ownership: 'evofw',
backend: 'nft',
action: 'drop',
raw: 'ip saddr @deny_v4 drop',
},
],
listeners: [{ protocol: 'tcp', port: 22, address: '0.0.0.0' }],
},
},
})
expect(filled.statusCode).toBe(200)
const empty = await app.inject({
method: 'POST',
url: '/v1/agent/apply-report',
headers: {
authorization: `Bearer ${token}`,
'content-type': 'application/json',
},
payload: {
status: 'ok',
host_firewall: { rules: [], listeners: [] },
},
})
expect(empty.statusCode).toBe(200)
const snap = await app.inject({
method: 'GET',
url: `/api/v1/agents/${agentId}/host-firewall`,
})
const body = snap.json() as {
rules: unknown[]
listeners: unknown[]
}
expect(body.rules).toHaveLength(1)
expect(body.listeners).toHaveLength(1)
})
it('filters invalid host_firewall rules without failing apply-report', async () => {
const app = await appPromise
await app.ready()
const { agentId, token } = await enrollApprovedLinux(
app,
'host-fw-filter',
'evofw_host_fw_filter_token_a',
)
const report = await app.inject({
method: 'POST',
url: '/v1/agent/apply-report',
headers: {
authorization: `Bearer ${token}`,
'content-type': 'application/json',
},
payload: {
status: 'ok',
host_firewall: {
rules: [
{ ownership: 'evofw', backend: 'nft', raw: 'ok rule' },
{ ownership: 'nope', backend: 'nft', raw: 'bad ownership' },
{ not: 'a rule' },
],
listeners: [
{ protocol: 'tcp', port: 443, address: '::' },
{ protocol: 'tcp', port: 99999, address: '1.1.1.1' },
],
},
},
})
expect(report.statusCode).toBe(200)
const snap = await app.inject({
method: 'GET',
url: `/api/v1/agents/${agentId}/host-firewall`,
})
const body = snap.json() as {
rules: unknown[]
listeners: { port: number }[]
}
expect(body.rules).toHaveLength(1)
expect(body.listeners).toHaveLength(1)
expect(body.listeners[0]?.port).toBe(443)
})
it('rejects port ACL on non-linux agents', async () => {
const app = await appPromise
await app.ready()