mirror of
https://github.com/hanzoai/brand.git
synced 2026-10-23 03:05:28 +00:00
normalizeHost strips trailing FQDN root dot(s) after the port strip, so a real brand FQDN (lux.network.) resolves to ITS brand instead of the default — without ever widening a match, since stripping root dots cannot change a host's registrable domain. Expand the resolver test across the attack surface: homoglyph/IDN/punycode, suffix-confusion, control-char/null/injection, IP/localhost, trailing-dot FQDN, multi-suffix, and a broad independent-model sweep. Every non-exact host falls to the default; no sibling-brand leak. 19/19 green.
260 lines
12 KiB
JavaScript
260 lines
12 KiB
JavaScript
/**
|
||
* White-label brand registry + resolver tests — run against the BUILT artifact
|
||
* (dist/index.mjs), so they assert what consumers actually import.
|
||
*
|
||
* The load-bearing property is the WHITE-LABEL INVARIANT: a host for one brand
|
||
* must NEVER resolve to another brand's identity or mark. Cross-contamination
|
||
* (a Lux surface showing the Hanzo H) is the threat these tests exist to catch.
|
||
*/
|
||
import test from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
|
||
import {
|
||
BRANDS,
|
||
BRAND_IDS,
|
||
BRAND_MARKS,
|
||
HOST_BRANDS,
|
||
brandFromHost,
|
||
getBrand,
|
||
isKnownBrandHost,
|
||
normalizeHost,
|
||
renderMarkSVG,
|
||
toBrandContract,
|
||
DEFAULT_BRAND,
|
||
} from '../dist/index.mjs'
|
||
|
||
test('every brand id has a complete identity record', () => {
|
||
for (const id of BRAND_IDS) {
|
||
const b = BRANDS[id]
|
||
assert.equal(b.id, id, `${id}: id field matches key`)
|
||
for (const f of ['name', 'title', 'tagline', 'legalName', 'domain', 'docsDomain', 'adminDomain', 'accentColor']) {
|
||
assert.ok(typeof b[f] === 'string' && b[f].length > 0, `${id}.${f} is a non-empty string`)
|
||
}
|
||
assert.ok(b.mark && b.mark.viewBox && b.mark.content, `${id} has a mark`)
|
||
assert.ok(b.theme.light && b.theme.dark, `${id} has light+dark theme`)
|
||
assert.ok(b.social && typeof b.social === 'object', `${id} has social`)
|
||
}
|
||
})
|
||
|
||
test('brandFromHost resolves each brand host to ITS brand — never another', () => {
|
||
const cases = [
|
||
['hanzo.ai', 'hanzo'],
|
||
['cloud.hanzo.ai', 'hanzo'],
|
||
['hanzo.id', 'hanzo'],
|
||
['hanzo.chat', 'hanzo'],
|
||
['lux.network', 'lux'],
|
||
['cloud.lux.network', 'lux'],
|
||
['lux.cloud', 'lux'],
|
||
['lux.id', 'lux'],
|
||
['zoo.ngo', 'zoo'],
|
||
['sub.zoo.ngo', 'zoo'],
|
||
['zoo.network', 'zoo'],
|
||
['zoolabs.id', 'zoo'],
|
||
['pars.network', 'pars'],
|
||
['pars.id', 'pars'],
|
||
['7stars.dev', '7stars'],
|
||
['console.7stars.dev', '7stars'],
|
||
['yotoda.tech', 'yotoda'],
|
||
]
|
||
for (const [host, want] of cases) {
|
||
assert.equal(brandFromHost(host), want, `${host} -> ${want}`)
|
||
}
|
||
})
|
||
|
||
test('resolution is port- and case-insensitive', () => {
|
||
assert.equal(brandFromHost('LUX.NETWORK:8443'), 'lux')
|
||
assert.equal(brandFromHost(' Cloud.Zoo.Ngo:3000 '), 'zoo')
|
||
assert.equal(normalizeHost('HANZO.AI:443'), 'hanzo.ai')
|
||
})
|
||
|
||
test('SECURITY: lookalike / suffix-confusion hosts never impersonate a brand', () => {
|
||
// A lookalike domain that merely CONTAINS a brand suffix must fall through to
|
||
// the default — it must not inherit lux/zoo/pars branding.
|
||
const attackers = [
|
||
'lux.network.evil.com', // suffix as a subdomain prefix of an attacker domain
|
||
'evillux.network', // no dot boundary before the suffix
|
||
'notzoo.ngo', // no dot boundary
|
||
'zoo.ngo.attacker.io', // brand suffix buried mid-host
|
||
'hanzo.ai.phish.net',
|
||
'xlux.cloud',
|
||
'pars.networkx', // suffix is a prefix of the real label
|
||
]
|
||
for (const host of attackers) {
|
||
assert.equal(brandFromHost(host), DEFAULT_BRAND, `${host} must NOT match a brand suffix`)
|
||
assert.equal(isKnownBrandHost(host), false, `${host} is not a known brand host`)
|
||
}
|
||
})
|
||
|
||
test('SECURITY: homoglyph / IDN / punycode hosts never impersonate a brand', () => {
|
||
// A Unicode lookalike of a brand host is a DIFFERENT byte string than the
|
||
// ASCII suffix, so it must fall through to the default — never inherit the
|
||
// brand it visually mimics.
|
||
const homoglyphs = [
|
||
'luх.network', // Cyrillic 'х' (U+0445) for ASCII 'x'
|
||
'lüx.network', // u-umlaut
|
||
'lux.nеtwork', // Cyrillic 'е' (U+0435) for ASCII 'e'
|
||
'lux.network', // fullwidth l/u/x
|
||
'zoo.ngο', // Greek omicron (U+03BF) for ASCII 'o'
|
||
'xn--lux-8cd.network', // a punycode label is not the ASCII suffix
|
||
]
|
||
for (const host of homoglyphs) {
|
||
assert.equal(brandFromHost(host), DEFAULT_BRAND, `${host} must NOT match a brand suffix`)
|
||
assert.equal(isKnownBrandHost(host), false, `${host} is not a known brand host`)
|
||
}
|
||
})
|
||
|
||
test('SECURITY: control-char / null / injection hosts never impersonate a brand', () => {
|
||
// Trim only strips leading/trailing whitespace; an embedded control char or a
|
||
// null byte keeps the host from matching a suffix -> default (fail-safe).
|
||
const junk = [
|
||
'lux.network |