feat(traffic-flow): enhance Instagram service handling and classification
Docker images / prepare-release (push) Successful in 9s
Docker images / backend-test (push) Successful in 2m34s
Docker images / frontend-image (push) Successful in 3m20s
Docker images / updater-image (push) Successful in 43s
Docker images / backend-image (push) Successful in 2m25s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 13s

- Added support for Instagram in traffic flow resolution and classification, including new test cases to validate its behavior.
- Introduced `INSTAGRAM` brand handling in the traffic flow logic, ensuring accurate service identification for Instagram-related traffic.
- Updated existing tests to cover various scenarios involving Instagram, including IP resolution and service categorization.
- Enhanced the service brand icon component to include an Instagram icon for better visual representation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Denozordec
2026-09-12 11:20:16 +07:00
co-authored by Cursor
parent ee9804f1bb
commit f3c846201c
5 changed files with 116 additions and 7 deletions
@@ -79,6 +79,33 @@ assert.equal(resolveFlowBrand("64.233.161.1", 0, "", 17, 443, 50000)?.service, "
assert.equal(resolveFlowBrand("64.233.161.1", 0, "", 6, 80, 50000)?.service, "Google")
assert.equal(resolveFlowBrand("2001:4860:4860::8888", 15169, "GOOGLE", 17, 53, 53000)?.service, "Google")
assert.equal(resolveFlowBrand("2001:4860:4860::8888", 15169, "GOOGLE", 17, 443, 50000)?.service, "YouTube")
assert.equal(brandByAsn(32934)?.service, "Meta")
assert.equal(lookupBrand("157.240.12.52", 0)?.service, "Meta")
assert.equal(lookupBrand("57.144.22.192", 0)?.service, "Meta")
assert.equal(brandByHolder("Instagram LLC")?.service, "Instagram")
assert.equal(mapServiceNodeId("Instagram"), "svc:instagram")
assert.equal(isNamedInternetService("Instagram", "Видео / стриминг"), true)
assert.equal(
resolveFlowBrand("157.240.12.52", 32934, "FACEBOOK", 6, 443, 51234)?.service,
"Instagram",
"HTTPS на Meta front → Instagram, как YouTube на Google",
)
assert.equal(
resolveFlowBrand("57.144.22.192", 0, "", 17, 443, 50000)?.service,
"Instagram",
"cdninstagram CIDR :443 без ASN → Instagram",
)
assert.equal(
resolveFlowBrand("157.240.12.52", 32934, "FACEBOOK", 6, 80, 50000)?.service,
"Meta",
":80 на Meta остаётся Meta",
)
assert.equal(
resolveFlowBrand("157.240.1.1", 54115, "WHATSAPP", 6, 443, 1)?.service,
"Meta",
"AS54115 WhatsApp не становится Instagram",
)
assert.equal(resolveRipeCountry("", 9059, ""), "IE")
assert.equal(resolveRipeCountry("", 24940, ""), "DE")
+36 -6
View File
@@ -39,6 +39,7 @@ const TIMEWEB: BrandHit = { service: "Timeweb", ...CDN }
const BEGET: BrandHit = { service: "Beget", ...CDN }
const DDOS_GUARD: BrandHit = { service: "DDoS-Guard", ...CDN }
const META: BrandHit = { service: "Meta", ...CDN }
const INSTAGRAM: BrandHit = { service: "Instagram", ...VIDEO }
const GOOGLE: BrandHit = { service: "Google", ...WEB }
const GITHUB: BrandHit = { service: "GitHub", ...WEB }
@@ -184,10 +185,27 @@ const CIDR_BRANDS: Array<{ cidr: string; prefixLen: number; hit: BrandHit }> = [
{ cidr: "216.239.32.0/19", prefixLen: 19, hit: GOOGLE },
{ cidr: "208.65.152.0/22", prefixLen: 22, hit: YOUTUBE },
{ cidr: "208.117.224.0/19", prefixLen: 19, hit: YOUTUBE },
{ cidr: "31.13.64.0/18", prefixLen: 18, hit: META },
{ cidr: "57.141.0.0/16", prefixLen: 16, hit: META },
{ cidr: "57.142.0.0/15", prefixLen: 15, hit: META },
{ cidr: "57.144.0.0/14", prefixLen: 14, hit: META },
{ cidr: "57.148.0.0/15", prefixLen: 15, hit: META },
{ cidr: "66.220.144.0/20", prefixLen: 20, hit: META },
{ cidr: "69.63.176.0/20", prefixLen: 20, hit: META },
{ cidr: "69.171.224.0/19", prefixLen: 19, hit: META },
{ cidr: "74.119.76.0/22", prefixLen: 22, hit: META },
{ cidr: "129.134.0.0/16", prefixLen: 16, hit: META },
{ cidr: "157.240.0.0/16", prefixLen: 16, hit: META },
{ cidr: "173.252.64.0/18", prefixLen: 18, hit: META },
{ cidr: "179.60.192.0/22", prefixLen: 22, hit: META },
{ cidr: "185.60.216.0/22", prefixLen: 22, hit: META },
{ cidr: "199.201.64.0/22", prefixLen: 22, hit: META },
{ cidr: "204.15.20.0/22", prefixLen: 22, hit: META },
].sort((a, b) => b.prefixLen - a.prefixLen)
const HOLDER_BRANDS: Array<{ re: RegExp; hit: BrandHit }> = [
{ re: /youtube/i, hit: YOUTUBE },
{ re: /instagram/i, hit: INSTAGRAM },
{ re: /valve|\bsteam\b/i, hit: STEAM },
{ re: /blizzard|battle.?net/i, hit: BLIZZARD },
{ re: /openai/i, hit: CHATGPT },
@@ -205,6 +223,9 @@ const NON_ISO = new Set(["EU", "AP", "ZZ", "XX", "A1", "A2", "O1"])
const STEAM_ASN = 32590
const GOOGLE_FRONT_ASN = new Set([15169, 396982])
/** AS32934 / AS63293 — Meta front (Facebook + Instagram CDN). AS54115 — WhatsApp, не Instagram. */
const META_FRONT_ASN = new Set([32934, 63293])
const WHATSAPP_ASN = 54115
function isGooglePublicDns(ip: string): boolean {
return ipInCidrV4(ip, "8.8.8.0/24") || ipInCidrV4(ip, "8.8.4.0/24")
@@ -274,9 +295,19 @@ export function lookupBrand(ip: string, asn: number): BrandHit | null {
return brandByCidr(ip) || brandByAsn(asn)
}
function isGoogleFront(asn: number, cidrBrand: BrandHit | null, asnBrand: BrandHit | null): boolean {
return GOOGLE_FRONT_ASN.has(asn) || cidrBrand?.service === "Google" || asnBrand?.service === "Google"
}
function isInstagramFront(asn: number, cidrBrand: BrandHit | null, asnBrand: BrandHit | null): boolean {
if (asn === WHATSAPP_ASN) return false
return META_FRONT_ASN.has(asn) || cidrBrand?.service === "Meta" || asnBrand?.service === "Meta"
}
/**
* Cloudflare CIDR бьёт holder (витрина на CF не становится Steam).
* Holder (YouTube и др.) бьёт остальные CIDR/ASN.
* Holder (YouTube / Instagram и др.) бьёт остальные CIDR/ASN.
* HTTPS/QUIC на Google front → YouTube (кроме 8.8.8.8); на Meta front → Instagram (кроме WhatsApp ASN).
* Порты Steam — только AS32590 и не выше Cloudflare CIDR.
*/
export function resolveFlowBrand(
@@ -292,13 +323,12 @@ export function resolveFlowBrand(
const holderBrand = brandByHolder(holder)
if (holderBrand) return holderBrand
const asnBrand = brandByAsn(asn)
if (
!isGooglePublicDns(ip)
&& isHttpsOrQuic(proto, dstPort, srcPort)
&& (GOOGLE_FRONT_ASN.has(asn) || cidrBrand?.service === "Google" || asnBrand?.service === "Google")
) {
if (!isGooglePublicDns(ip) && isHttpsOrQuic(proto, dstPort, srcPort) && isGoogleFront(asn, cidrBrand, asnBrand)) {
return YOUTUBE
}
if (isHttpsOrQuic(proto, dstPort, srcPort) && isInstagramFront(asn, cidrBrand, asnBrand)) {
return INSTAGRAM
}
const fromLookup = cidrBrand || asnBrand
if (fromLookup) return fromLookup
if (asn === STEAM_ASN && isSteamGamePort(proto, dstPort, srcPort)) return STEAM
@@ -155,6 +155,50 @@ const ipv6Yt = classifyFlowDst("2001:4860:4860::8888", 17, 443, 50000, {
fetchedAt: Date.now(),
})
assert.equal(ipv6Yt.service, "YouTube")
const instagram = classifyFlowDst("157.240.12.52", 6, 443, 62598, {
prefix: "157.240.0.0/16",
asn: 32934,
country: "US",
lat: null,
lng: null,
holder: "FACEBOOK",
ok: true,
fetchedAt: Date.now(),
})
assert.equal(instagram.service, "Instagram")
assert.equal(instagram.category, "Видео / стриминг")
assert.notEqual(instagram.service, "Meta")
const metaHttp = classifyFlowDst("157.240.12.52", 6, 80, 50000, {
prefix: "157.240.0.0/16",
asn: 32934,
country: "US",
lat: null,
lng: null,
holder: "FACEBOOK",
ok: true,
fetchedAt: Date.now(),
})
assert.equal(metaHttp.service, "Meta")
assert.equal(metaHttp.category, "CDN")
const igHolder = classifyFlowDst("203.0.113.80", 6, 443, 1, {
prefix: "203.0.113.0/24",
asn: 64503,
country: "US",
lat: null,
lng: null,
holder: "Instagram LLC",
ok: true,
fetchedAt: Date.now(),
})
assert.equal(igHolder.service, "Instagram")
const igCidr = classifyFlowDst("57.144.22.192", 17, 443, 50000, null)
assert.equal(igCidr.service, "Instagram")
assert.equal(igCidr.category, "Видео / стриминг")
const esp = classifyFlowDst("198.51.100.1", 50, 0, 0, null)
assert.equal(esp.category, "Туннель")
assert.equal(applicationName(17, 443, 50000), "QUIC")
@@ -48,7 +48,7 @@ export function seedFlowCatalogForTests(input: {
export function categoryFromPurpose(purpose: string, proto: number, dstPort: number, srcPort: number): string {
const p = purpose.toLowerCase()
if (/gaming|steam|epic|riot|playstation|roblox|ubisoft/.test(p)) return "Игры"
if (/streaming|youtube|netflix|twitch|video|spotify/.test(p)) return "Видео / стриминг"
if (/streaming|youtube|netflix|twitch|video|spotify|instagram/.test(p)) return "Видео / стриминг"
if (/cdn|cloudflare|akamai|fastly|hetzner|ovh|apple/.test(p)) return "CDN"
if (/voip|discord|zoom/.test(p)) return "Голос"
if (/openai|chatgpt|\bai\b/.test(p)) return "ИИ"
@@ -74,6 +74,14 @@ export function ServiceBrandIcon({ label, size = 22 }: { label: string; size?: n
<path d="M10.2 9.2v5.6L15.6 12Z" fill="#fff" />
</BrandSvg>
)
case "instagram":
return (
<BrandSvg size={size}>
<rect x="3" y="3" width="18" height="18" rx="5" fill="#E4405F" />
<circle cx="12" cy="12.2" r="4.1" fill="none" stroke="#fff" strokeWidth="1.8" />
<circle cx="16.3" cy="7.7" r="1.15" fill="#fff" />
</BrandSvg>
)
case "netflix":
return (
<BrandSvg size={size}>