Docker images / prepare-release (push) Successful in 8s
Docker images / backend-test (push) Successful in 2m29s
Docker images / frontend-image (push) Successful in 3m20s
Docker images / updater-image (push) Successful in 51s
Docker images / backend-image (push) Successful in 2m40s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 13s
- Introduced `canonicalIp` function to standardize IP address formats across the application, improving consistency in flow processing. - Updated traffic flow analytics to utilize new endpoint resolution logic, enhancing accuracy in traffic classification. - Enhanced tests for traffic flow analytics and IP handling, ensuring comprehensive coverage for new functionalities. - Improved traffic flow destination resolution with additional test cases for various internet services, including Google and Fastly. Co-authored-by: Cursor <cursoragent@cursor.com>
257 lines
6.4 KiB
TypeScript
257 lines
6.4 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import {
|
|
ingestParsedFlowsForServerForTests,
|
|
resetEngineForTests,
|
|
} from "./traffic-flow-engine.js"
|
|
import { factsSnapshotForTests } from "./traffic-flow-facts.js"
|
|
import { classifyInternetBrand, mapInternetBrand, resolveInternetDest } from "./traffic-flow-dest.js"
|
|
import { disableCatalogFetchForTests, resetFlowCatalogForTests } from "./traffic-flow-classify.js"
|
|
import {
|
|
disableRipeEnqueueForTests,
|
|
disableRipePersistForTests,
|
|
resetRipeCacheForTests,
|
|
seedRipeCacheForTests,
|
|
} from "./traffic-flow-ripe.js"
|
|
import { seedFlowTopologyForTests, type FlowTopology } from "./traffic-flow-topology.js"
|
|
import { rememberServerIfaces, resetIfaceCacheForTests } from "./traffic-flow-ifindex.js"
|
|
|
|
disableCatalogFetchForTests()
|
|
resetFlowCatalogForTests()
|
|
disableRipePersistForTests()
|
|
disableRipeEnqueueForTests()
|
|
resetRipeCacheForTests()
|
|
resetEngineForTests()
|
|
resetIfaceCacheForTests()
|
|
|
|
seedRipeCacheForTests({
|
|
prefix: "8.8.8.0/24",
|
|
asn: 15169,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "GOOGLE",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
})
|
|
seedRipeCacheForTests({
|
|
prefix: "95.167.0.0/16",
|
|
asn: 12389,
|
|
country: "RU",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "ROSTELECOM-AS",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
})
|
|
|
|
const topo: FlowTopology = {
|
|
clientIfaces: new Map([[1, new Set(["gre-client"])]]),
|
|
clientByIface: new Map([["1|gre-client", {
|
|
userId: "u-rost",
|
|
login: "alice",
|
|
name: "Alice",
|
|
serverId: 1,
|
|
interfaceName: "gre-client",
|
|
}]]),
|
|
enNodes: [{ id: 2, name: "en", hosts: ["198.51.100.1"] }],
|
|
enHosts: new Set(["198.51.100.1"]),
|
|
jhHosts: new Set(["203.0.113.10"]),
|
|
wanIfaces: new Map([[1, new Set(["ether1"])]]),
|
|
plane: {
|
|
clientIfaceNames: new Set(["gre-client"]),
|
|
enHosts: new Set(["198.51.100.1"]),
|
|
jhHosts: new Set(["203.0.113.10"]),
|
|
},
|
|
}
|
|
seedFlowTopologyForTests(topo)
|
|
rememberServerIfaces(1, [{ name: "gre-client", ifindex: "2" }])
|
|
|
|
ingestParsedFlowsForServerForTests(1, [
|
|
{
|
|
src: "95.167.1.10",
|
|
dst: "10.200.100.53",
|
|
proto: 6,
|
|
srcPort: 51234,
|
|
dstPort: 443,
|
|
bytes: 100,
|
|
packets: 2,
|
|
inIface: "gre-client",
|
|
outIface: "ether1",
|
|
},
|
|
{
|
|
src: "95.167.1.10",
|
|
dst: "8.8.8.8",
|
|
proto: 6,
|
|
srcPort: 51234,
|
|
dstPort: 443,
|
|
bytes: 50,
|
|
packets: 1,
|
|
inIface: "gre-client",
|
|
outIface: "ether1",
|
|
},
|
|
{
|
|
src: "203.0.113.10",
|
|
dst: "198.51.100.1",
|
|
proto: 47,
|
|
srcPort: 0,
|
|
dstPort: 0,
|
|
bytes: 9_000,
|
|
packets: 90,
|
|
inIface: "NSK-SERVHOST-RTK",
|
|
outIface: "NSK-SERVHOST-RTK",
|
|
},
|
|
{
|
|
src: "10.200.100.53",
|
|
dst: "10.200.100.1",
|
|
proto: 6,
|
|
srcPort: 53880,
|
|
dstPort: 443,
|
|
bytes: 70,
|
|
packets: 1,
|
|
inIface: "gre-client",
|
|
outIface: "ether1",
|
|
natDst: "8.8.8.8",
|
|
natDstPort: 443,
|
|
},
|
|
])
|
|
|
|
const facts = factsSnapshotForTests()
|
|
const total = facts.reduce((s, r) => s + r.bytes, 0)
|
|
assert.equal(total, 120, "unique = Google payload + NAT, без overlay/пустого dest")
|
|
assert.equal(facts.some((r) => r.asn === 12389), false, "ASN клиента не в кубе")
|
|
assert.equal(facts.some((r) => r.service === "GRE"), false, "GRE не сервис unique")
|
|
const google = facts.find((r) => r.asn === 15169)
|
|
assert.ok(google)
|
|
assert.equal(google.bytes, 120)
|
|
assert.equal(facts.filter((r) => r.asn === 0).reduce((s, r) => s + r.bytes, 0), 0)
|
|
|
|
assert.equal(classifyInternetBrand("8.8.8.8", 47, 0, 0, null), null, "GRE не бренд")
|
|
assert.equal(classifyInternetBrand("8.8.8.8", 6, 443, 51234, {
|
|
prefix: "8.8.8.0/24",
|
|
asn: 15169,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "GOOGLE",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
})?.service, "Google")
|
|
assert.equal(mapInternetBrand("203.0.113.50", 6, 443, 51234, null).service, "Прочее")
|
|
assert.equal(mapInternetBrand("8.8.8.8", 47, 0, 0, null).service, "Прочее")
|
|
assert.equal(mapInternetBrand("8.8.8.8", 6, 443, 51234, {
|
|
prefix: "8.8.8.0/24",
|
|
asn: 15169,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "GOOGLE",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
}).service, "Google")
|
|
|
|
assert.equal(classifyInternetBrand("8.8.8.8", 17, 53, 53000, {
|
|
prefix: "8.8.8.0/24",
|
|
asn: 15169,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "GOOGLE",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
})?.service, "Google")
|
|
assert.equal(mapInternetBrand("8.8.8.8", 17, 53, 53000, {
|
|
prefix: "8.8.8.0/24",
|
|
asn: 15169,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "GOOGLE",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
}).service, "Google")
|
|
assert.equal(mapInternetBrand("2001:4860:4860::8888", 17, 443, 50000, {
|
|
prefix: "2001:4860:4860::8888/128",
|
|
asn: 15169,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "GOOGLE",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
}).service, "YouTube")
|
|
assert.equal(mapInternetBrand("64.233.161.1", 17, 443, 50000, null).service, "YouTube")
|
|
assert.equal(mapInternetBrand("142.250.1.10", 6, 443, 1, null).service, "YouTube")
|
|
|
|
{
|
|
const meta = resolveInternetDest({
|
|
src: "74.125.104.196/32",
|
|
dst: "10.200.100.53/32",
|
|
proto: 17,
|
|
srcPort: 443,
|
|
dstPort: 62598,
|
|
serverId: 1,
|
|
inIface: "gre-client",
|
|
topo,
|
|
})
|
|
assert.equal(meta.dest, "74.125.104.196")
|
|
assert.equal(meta.classified.service, "YouTube")
|
|
assert.notEqual(meta.classified.service, "Прочее")
|
|
assert.equal(meta.endpoints.direction, "to_client")
|
|
assert.equal(meta.endpoints.clientIp, "10.200.100.53")
|
|
assert.equal(meta.asn, 0)
|
|
}
|
|
|
|
{
|
|
seedRipeCacheForTests({
|
|
prefix: "146.75.0.0/16",
|
|
asn: 54113,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "FASTLY",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
})
|
|
seedRipeCacheForTests({
|
|
prefix: "3.174.0.0/16",
|
|
asn: 16509,
|
|
country: "US",
|
|
lat: null,
|
|
lng: null,
|
|
holder: "AMAZON-AES",
|
|
ok: true,
|
|
fetchedAt: Date.now(),
|
|
})
|
|
const fastly = resolveInternetDest({
|
|
src: "146.75.118.132/32",
|
|
dst: "10.200.100.53/32",
|
|
proto: 6,
|
|
srcPort: 80,
|
|
dstPort: 35026,
|
|
serverId: 1,
|
|
inIface: "gre-client",
|
|
topo,
|
|
})
|
|
assert.equal(fastly.classified.service, "Fastly")
|
|
assert.equal(fastly.asn, 54113)
|
|
assert.equal(fastly.country, "US")
|
|
const aws = resolveInternetDest({
|
|
src: "3.174.2.35/32",
|
|
dst: "10.200.100.53/32",
|
|
proto: 6,
|
|
srcPort: 443,
|
|
dstPort: 43726,
|
|
serverId: 1,
|
|
inIface: "gre-client",
|
|
topo,
|
|
})
|
|
assert.equal(aws.classified.service, "AWS")
|
|
assert.equal(aws.asn, 16509)
|
|
}
|
|
|
|
resetEngineForTests()
|
|
seedFlowTopologyForTests(null)
|
|
resetRipeCacheForTests()
|
|
resetIfaceCacheForTests()
|
|
console.log("traffic-flow-dest.test.ts: ok")
|