feat(api): unify policy handling with default action updates
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m53s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

- Updated `evofw-firewall.sh` and related scripts to replace `policy_mode` with `default_action`, enhancing clarity and consistency in policy management.
- Adjusted agent routes and evaluation logic to accommodate the new default action structure, ensuring backward compatibility with legacy modes.
- Enhanced tests to validate the new default action behavior and its integration within the agent policy framework.
- Refactored related components in the web interface to align with the updated policy handling, improving user experience and reducing confusion around policy modes.
This commit is contained in:
Denozordec committed 2026-07-23 10:52:28 +07:00
1 parent a6eb21a10d
commit 1f7273f38d
30 files changed
+1469 -621

No files matched your search

@@ -16,7 +16,25 @@ const testConfig: AppConfig = {
enrollSeed: 'test-seed',
}
describe('policy set mode + rules', () => {
async function createAgent(
app: Awaited<ReturnType<typeof buildApp>>,
name: string,
) {
const link = await app.inject({
method: 'POST',
url: '/api/v1/install-links',
payload: { name, platform: 'linux' },
})
expect(link.statusCode).toBe(201)
const agentId = (link.json() as { agent_id: string }).agent_id
await app.inject({
method: 'POST',
url: `/api/v1/agents/${agentId}/approve`,
})
return agentId
}
describe('classic policy default_action', () => {
const appPromise = buildApp({ memory: true, config: testConfig })
afterAll(async () => {
@@ -24,104 +42,99 @@ describe('policy set mode + rules', () => {
await app.close()
})
it('set policy_mode and reorder; disabled rules skipped in policy', async () => {
it('mixed deny/allow; deny wins exact; preview has default_action', async () => {
const app = await appPromise
await app.ready()
const created = await app.inject({
method: 'POST',
url: '/api/v1/policy-sets',
payload: {
name: 'WL set',
policy_mode: 'whitelist',
},
payload: { name: 'mixed-set' },
})
expect(created.statusCode).toBe(200)
const set = created.json() as { id: string; policy_mode: string }
expect(set.policy_mode).toBe('whitelist')
const setId = (created.json() as { id: string }).id
const r1 = await app.inject({
method: 'POST',
url: '/api/v1/rules',
payload: {
set_id: set.id,
action: 'allow',
cidr: '10.0.0.1/32',
},
})
expect(r1.statusCode).toBe(200)
const rule1 = r1.json() as { id: string; enabled: boolean; priority: number }
for (const payload of [
{ set_id: setId, action: 'deny', cidr: '10.0.0.1/32' },
{ set_id: setId, action: 'allow', cidr: '10.0.0.1/32' },
{ set_id: setId, action: 'allow', cidr: '10.0.0.2/32' },
]) {
const r = await app.inject({
method: 'POST',
url: '/api/v1/rules',
payload,
})
expect(r.statusCode).toBe(200)
}
const r2 = await app.inject({
method: 'POST',
url: '/api/v1/rules',
payload: {
set_id: set.id,
action: 'allow',
cidr: '10.0.0.2/32',
},
})
const rule2 = r2.json() as { id: string }
const agentId = await createAgent(app, 'pol-agent')
const reordered = await app.inject({
method: 'PUT',
url: `/api/v1/policy-sets/${set.id}/rules/reorder`,
payload: { ordered_ids: [rule2.id, rule1.id] },
})
expect(reordered.statusCode).toBe(200)
const items = (
reordered.json() as { items: { id: string; priority: number }[] }
).items
expect(items[0]?.id).toBe(rule2.id)
expect(items[0]!.priority).toBeLessThan(items[1]!.priority)
await app.inject({
method: 'PATCH',
url: `/api/v1/rules/${rule1.id}`,
payload: { enabled: false },
})
// enroll + approve agent, assign set
const enroll = await app.inject({
method: 'POST',
url: '/v1/agent/enroll',
headers: {
'content-type': 'application/json',
'x-evofw-seed': 'test-seed',
},
payload: {
name: 'mt-wl',
platform: 'linux',
token: 'evofw_policy_mode_token_abcdef12',
},
})
const agent = enroll.json() as { id: string }
await app.inject({
method: 'POST',
url: `/api/v1/agents/${agent.id}/approve`,
})
const assign = await app.inject({
method: 'PUT',
url: `/api/v1/agents/${agent.id}/policy-sets`,
payload: { set_ids: [set.id] },
url: `/api/v1/agents/${agentId}/policy-sets`,
payload: { set_ids: [setId] },
})
expect(assign.statusCode).toBe(200)
const policy = await app.inject({
method: 'GET',
url: '/v1/agent/policy',
headers: {
authorization: 'Bearer evofw_policy_mode_token_abcdef12',
},
const patch = await app.inject({
method: 'PATCH',
url: `/api/v1/agents/${agentId}`,
payload: { default_action: 'drop' },
})
expect(policy.statusCode).toBe(200)
const body = policy.json() as {
policy_mode: string
expect(patch.statusCode).toBe(200)
expect((patch.json() as { default_action: string }).default_action).toBe(
'drop',
)
const preview = await app.inject({
method: 'GET',
url: `/api/v1/agents/${agentId}/preview`,
})
expect(preview.statusCode).toBe(200)
const body = preview.json() as {
default_action: string
apply_version: number
deny_cidrs: string[]
allow_cidrs: string[]
summary: { conflicts_dropped: number }
chain: unknown[]
}
expect(body.policy_mode).toBe('whitelist')
expect(body.allow_cidrs).toContain('10.0.0.2/32')
expect(body.default_action).toBe('drop')
expect(body.apply_version).toBe(2)
expect(body.deny_cidrs).toContain('10.0.0.1/32')
expect(body.allow_cidrs).not.toContain('10.0.0.1/32')
expect(rule1.enabled).toBe(true)
expect(body.allow_cidrs).toContain('10.0.0.2/32')
expect(body.summary.conflicts_dropped).toBeGreaterThanOrEqual(1)
expect(body.chain.length).toBeGreaterThanOrEqual(3)
})
it('allows assigning sets without same-mode lock', async () => {
const app = await appPromise
await app.ready()
const a = await app.inject({
method: 'POST',
url: '/api/v1/policy-sets',
payload: { name: 'set-a', policy_mode: 'blacklist' },
})
const b = await app.inject({
method: 'POST',
url: '/api/v1/policy-sets',
payload: { name: 'set-b', policy_mode: 'whitelist' },
})
expect(a.statusCode).toBe(200)
expect(b.statusCode).toBe(200)
const setA = (a.json() as { id: string }).id
const setB = (b.json() as { id: string }).id
const agentId = await createAgent(app, 'multi-mode-agent')
const assign = await app.inject({
method: 'PUT',
url: `/api/v1/agents/${agentId}/policy-sets`,
payload: { set_ids: [setA, setB] },
})
expect(assign.statusCode).toBe(200)
expect((assign.json() as { items: unknown[] }).items).toHaveLength(2)
})
})