mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(pcap): attribute DNS addresses by record owner
This commit is contained in:
1 parent
e152349c5d
commit
25375924f7
8 files changed
+429
-21
No files matched your search
@@ -81,9 +81,13 @@ Its separate inferred truncation lengths do not replace that limit; an unset
|
||||
file-header limit remains `null`.
|
||||
|
||||
Addresses count once per packet, including loopback and nested IP occurrences.
|
||||
DNS addresses are associated only with one question in the same response packet;
|
||||
additional records may be included, so this does not prove final resolution.
|
||||
Ambiguous addresses remain in a separate inventory. Protocol distribution counts
|
||||
DNS address attribution requires one question in one response and complete,
|
||||
consistent RR owner/type and section counts. Only answer-section A/AAAA records
|
||||
matching that question (or its unambiguous answer-section CNAME chain) are
|
||||
associated, with the requested address type respected. Authority, additional,
|
||||
unrelated, malformed, and ambiguous addresses remain in the unattributed
|
||||
inventory and make coverage partial. These packet-local relationships do not
|
||||
prove final resolution or destination authenticity. Protocol distribution counts
|
||||
exact nonempty `frame.protocols` stack strings. Frames for which TShark reports
|
||||
an empty stack, including zero-length packets, still contribute to the packet
|
||||
count without adding a protocol row. An all-empty stack inventory can have
|
||||
|
||||
@@ -348,10 +348,12 @@ describe("parseTsharkJsonOutput", () => {
|
||||
expect(result.dnsQueries).toEqual([
|
||||
{
|
||||
query: "example.com",
|
||||
response_addresses: ["93.184.216.34"],
|
||||
response_address_count: 1,
|
||||
response_addresses: [],
|
||||
response_address_count: 0,
|
||||
},
|
||||
]);
|
||||
expect(result.unattributedDnsAddresses).toEqual(["93.184.216.34"]);
|
||||
expect(result.partial).toBe(true);
|
||||
expect(result.tlsSniValues).toEqual(["example.com"]);
|
||||
expect(result.httpRequestUris).toEqual(["http://example.com/page"]);
|
||||
expect(result.protocolDistribution).toEqual(
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
type Fields = (name: string) => readonly string[];
|
||||
interface Attribution {
|
||||
readonly incomplete: boolean;
|
||||
readonly assigned: readonly (readonly [string, string])[];
|
||||
readonly unattributed: readonly string[];
|
||||
}
|
||||
|
||||
const dnsType = (value: string | undefined): number | null => {
|
||||
if (value === undefined || !/^(?:[0-9]+|0x[0-9a-f]+)$/iu.test(value))
|
||||
return null;
|
||||
const type = Number(value);
|
||||
return Number.isInteger(type) && type <= 65535 ? type : null;
|
||||
};
|
||||
|
||||
// DNS compares ASCII letters without changing retained producer spellings.
|
||||
const nameKey = (name: string): string =>
|
||||
name.replace(/[A-Z]/gu, (letter) => letter.toLowerCase());
|
||||
|
||||
/**
|
||||
* TShark visits answer, authority, then additional RRs, emitting one
|
||||
* dns.resp.name/type per RR (including OPT), and type-specific value queues.
|
||||
* NSEC/NXT bitmaps may add type fields; the cardinality check declines those
|
||||
* ambiguous flat inventories instead of reconstructing missing hierarchy.
|
||||
* Validate that correspondence before joining any owner to an address.
|
||||
*/
|
||||
export const attributeDnsAddresses = (field: Fields): Attribution => {
|
||||
const ipv4 = field("dns.a");
|
||||
const ipv6 = field("dns.aaaa");
|
||||
const addresses = [...ipv4, ...ipv6].filter((value) => value !== "");
|
||||
const unknown = (): Attribution => ({
|
||||
assigned: [],
|
||||
unattributed: addresses,
|
||||
incomplete:
|
||||
addresses.length > 0 ||
|
||||
field("dns.flags.response").some(
|
||||
(value) => value !== "0" && value !== "False",
|
||||
) ||
|
||||
field("dns.resp.name").length > 0 ||
|
||||
field("dns.resp.type").length > 0 ||
|
||||
["dns.count.answers", "dns.count.auth_rr", "dns.count.add_rr"].some(
|
||||
(key) => field(key).some((value) => value !== "0"),
|
||||
),
|
||||
});
|
||||
const count = (key: string): number | null => {
|
||||
const values = field(key);
|
||||
if (values.length !== 1 || !/^\d+$/u.test(values[0]!)) return null;
|
||||
const value = Number(values[0]);
|
||||
return Number.isInteger(value) && value <= 65535 ? value : null;
|
||||
};
|
||||
const response = field("dns.flags.response");
|
||||
const questions = field("dns.qry.name");
|
||||
const questionTypes = field("dns.qry.type");
|
||||
const answers = count("dns.count.answers");
|
||||
const authority = count("dns.count.auth_rr");
|
||||
const additional = count("dns.count.add_rr");
|
||||
if (answers === null || authority === null || additional === null)
|
||||
return unknown();
|
||||
const owners = field("dns.resp.name");
|
||||
const types = field("dns.resp.type").map(dnsType);
|
||||
const aliases = field("dns.cname");
|
||||
if (
|
||||
owners.length !== answers + authority + additional ||
|
||||
types.length !== owners.length ||
|
||||
types.some((value) => value === null) ||
|
||||
types.filter((type) => type === 1).length !== ipv4.length ||
|
||||
types.filter((type) => type === 28).length !== ipv6.length ||
|
||||
types.filter((type) => type === 5).length !== aliases.length ||
|
||||
[...ipv4, ...ipv6, ...aliases].some((value) => value === "")
|
||||
)
|
||||
return unknown();
|
||||
if (addresses.length === 0)
|
||||
return { assigned: [], unattributed: [], incomplete: false };
|
||||
if (
|
||||
response.length !== 1 ||
|
||||
!["1", "True"].includes(response[0]!) ||
|
||||
count("dns.count.queries") !== 1 ||
|
||||
questions.length !== 1 ||
|
||||
questions[0] === "" ||
|
||||
questionTypes.length !== 1 ||
|
||||
![1, 28, 255].includes(dnsType(questionTypes[0]) ?? -1)
|
||||
)
|
||||
return unknown();
|
||||
let a = 0,
|
||||
aaaa = 0,
|
||||
cname = 0;
|
||||
const records = types.map((type, index) => ({
|
||||
owner: nameKey(owners[index]!),
|
||||
type,
|
||||
answer: index < answers,
|
||||
value:
|
||||
type === 1
|
||||
? ipv4[a++]
|
||||
: type === 28
|
||||
? ipv6[aaaa++]
|
||||
: type === 5
|
||||
? aliases[cname++]
|
||||
: undefined,
|
||||
}));
|
||||
const reachable = new Set([nameKey(questions[0]!)]);
|
||||
const links = new Map<string, Set<string>>();
|
||||
for (const record of records)
|
||||
if (record.answer && record.type === 5) {
|
||||
if (record.owner === "") return unknown();
|
||||
const targets = links.get(record.owner) ?? new Set<string>();
|
||||
targets.add(nameKey(record.value!));
|
||||
links.set(record.owner, targets);
|
||||
}
|
||||
// A conflicting CNAME does not establish which target the question resolves.
|
||||
let current = nameKey(questions[0]!);
|
||||
while (links.has(current)) {
|
||||
const targets = links.get(current)!;
|
||||
if (targets.size !== 1) return unknown();
|
||||
const next = targets.values().next().value!;
|
||||
if (reachable.has(next)) return unknown();
|
||||
reachable.add(next);
|
||||
current = next;
|
||||
}
|
||||
const assigned: (readonly [string, string])[] = [];
|
||||
const unattributed: string[] = [];
|
||||
for (const record of records) {
|
||||
if (record.type !== 1 && record.type !== 28) continue;
|
||||
if (
|
||||
record.answer &&
|
||||
record.owner !== "" &&
|
||||
reachable.has(record.owner) &&
|
||||
(dnsType(questionTypes[0]) === 255 ||
|
||||
dnsType(questionTypes[0]) === record.type) &&
|
||||
!links.has(record.owner)
|
||||
)
|
||||
assigned.push([questions[0]!, record.value!]);
|
||||
else unattributed.push(record.value!);
|
||||
}
|
||||
return { assigned, unattributed, incomplete: unattributed.length > 0 };
|
||||
};
|
||||
@@ -16,6 +16,228 @@ const capture = (layers: ReadonlyArray<Record<string, string[]>>) =>
|
||||
})),
|
||||
);
|
||||
|
||||
// Representative selected-field output for one A reply with a CNAME answer
|
||||
// and unrelated additional glue. This is not a recorded real-provider run.
|
||||
const cnameAndGlue = {
|
||||
"dns.flags.response": ["1"],
|
||||
"dns.qry.name": ["www.example"],
|
||||
"dns.qry.type": ["1"],
|
||||
"dns.count.queries": ["1"],
|
||||
"dns.count.answers": ["2"],
|
||||
"dns.count.auth_rr": ["0"],
|
||||
"dns.count.add_rr": ["1"],
|
||||
"dns.resp.name": ["www.example", "edge.example", "ns.example"],
|
||||
"dns.resp.type": ["5", "1", "1"],
|
||||
"dns.cname": ["edge.example"],
|
||||
"dns.a": ["192.0.2.10", "192.0.2.53"],
|
||||
};
|
||||
|
||||
it("reports unknown RR coverage when a DNS response omits all count and address fields", async () => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([
|
||||
{
|
||||
"dns.flags.response": ["1"],
|
||||
"dns.qry.name": ["www.example"],
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(result.dnsQueries[0]?.response_addresses).toEqual([]);
|
||||
expect(result.unattributedDnsAddresses).toEqual([]);
|
||||
expect(result.partial).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps a query with an additional OPT record complete", async () => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([
|
||||
{
|
||||
...cnameAndGlue,
|
||||
"dns.flags.response": ["0"],
|
||||
"dns.count.answers": ["0"],
|
||||
"dns.resp.name": [""],
|
||||
"dns.resp.type": ["41"],
|
||||
"dns.cname": [],
|
||||
"dns.a": [],
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(result.partial).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves a CNAME answer while separating additional glue", async () => {
|
||||
const result = await parseTsharkJsonOutput(capture([cnameAndGlue]));
|
||||
expect(result.dnsQueries).toEqual([
|
||||
{
|
||||
query: "www.example",
|
||||
response_addresses: ["192.0.2.10"],
|
||||
response_address_count: 1,
|
||||
},
|
||||
]);
|
||||
expect(result.unattributedDnsAddresses).toEqual(["192.0.2.53"]);
|
||||
expect(result.unattributedDnsPackets).toBe(1);
|
||||
expect(result.partial).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts hexadecimal RR type codes without changing recorded identities", async () => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([
|
||||
{
|
||||
...cnameAndGlue,
|
||||
"dns.qry.type": ["0x0001"],
|
||||
"dns.resp.type": ["0x0005", "0x0001", "0x0001"],
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(result.dnsQueries[0]?.response_addresses).toEqual(["192.0.2.10"]);
|
||||
expect(result.unattributedDnsAddresses).toEqual(["192.0.2.53"]);
|
||||
});
|
||||
|
||||
it("retains direct answers with a root-owned OPT and compares DNS names without rewriting them", async () => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([
|
||||
{
|
||||
...cnameAndGlue,
|
||||
"dns.qry.name": ["WWW.Example"],
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.resp.name": ["www.example", ""],
|
||||
"dns.resp.type": ["1", "41"],
|
||||
"dns.cname": [],
|
||||
"dns.a": ["192.0.2.10"],
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(result.dnsQueries).toEqual([
|
||||
{
|
||||
query: "WWW.Example",
|
||||
response_addresses: ["192.0.2.10"],
|
||||
response_address_count: 1,
|
||||
},
|
||||
]);
|
||||
expect(result.partial).toBe(false);
|
||||
});
|
||||
|
||||
it("joins mixed answer types using their own queues and preserves multiple valid answers", async () => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([
|
||||
{
|
||||
...cnameAndGlue,
|
||||
"dns.qry.type": ["255"],
|
||||
"dns.count.answers": ["5"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": [
|
||||
"www.example",
|
||||
"edge.example",
|
||||
"edge.example",
|
||||
"edge.example",
|
||||
"edge.example",
|
||||
],
|
||||
"dns.resp.type": ["5", "28", "1", "28", "1"],
|
||||
"dns.a": ["192.0.2.10", "192.0.2.11"],
|
||||
"dns.aaaa": ["2001:db8::10", "2001:db8::11"],
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(result.dnsQueries[0]?.response_addresses).toEqual([
|
||||
"2001:db8::10",
|
||||
"192.0.2.10",
|
||||
"2001:db8::11",
|
||||
"192.0.2.11",
|
||||
]);
|
||||
expect(result.partial).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ "dns.resp.name": ["www.example", "unrelated.example", "ns.example"] },
|
||||
{ "dns.count.answers": ["1"], "dns.count.auth_rr": ["1"] },
|
||||
{ "dns.qry.type": ["28"] },
|
||||
{ "dns.flags.response": ["1", "1"] },
|
||||
{ "dns.count.answers": ["garbage"] },
|
||||
{ "dns.count.answers": ["4"] },
|
||||
{ "dns.count.answers": [] },
|
||||
{ "dns.resp.name": ["www.example"] },
|
||||
{ "dns.resp.type": ["5", "28", "1"] },
|
||||
{ "dns.cname": [] },
|
||||
{ "dns.cname": ["www.example"] },
|
||||
{ "dns.count.queries": ["2"] },
|
||||
// NSEC/NXT type bitmaps can contribute additional dns.resp.type fields.
|
||||
{ "dns.resp.type": ["5", "1", "47", "1", "28"] },
|
||||
] as Record<string, string[]>[])(
|
||||
"retains all addresses when ownership is unsupported or inconsistent: %j",
|
||||
async (override) => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([{ ...cnameAndGlue, ...override }]),
|
||||
);
|
||||
expect(result.dnsQueries[0]?.response_addresses).toEqual([]);
|
||||
expect(result.unattributedDnsAddresses).toEqual([
|
||||
"192.0.2.10",
|
||||
"192.0.2.53",
|
||||
]);
|
||||
expect(result.partial).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it("does not follow conflicting CNAME declarations", async () => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([
|
||||
{
|
||||
...cnameAndGlue,
|
||||
"dns.count.answers": ["4"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": [
|
||||
"www.example",
|
||||
"www.example",
|
||||
"edge.example",
|
||||
"other.example",
|
||||
],
|
||||
"dns.resp.type": ["5", "5", "1", "1"],
|
||||
"dns.cname": ["edge.example", "other.example"],
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(result.dnsQueries[0]?.response_addresses).toEqual([]);
|
||||
expect(result.unattributedDnsAddresses).toEqual(["192.0.2.10", "192.0.2.53"]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ "dns.a": [] },
|
||||
{ "dns.a": [], "dns.resp.name": [], "dns.resp.type": [] },
|
||||
])(
|
||||
"marks missing address/owner decoding partial even when no addresses were extracted: %j",
|
||||
async (override) => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([{ ...cnameAndGlue, ...override }]),
|
||||
);
|
||||
expect(result.dnsQueries[0]?.response_addresses).toEqual([]);
|
||||
expect(result.unattributedDnsAddresses).toEqual([]);
|
||||
expect(result.partial).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{ "dns.count.answers": ["0"], "dns.resp.name": [], "dns.resp.type": [] },
|
||||
{
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.resp.name": ["www.example"],
|
||||
"dns.resp.type": ["2"],
|
||||
},
|
||||
])(
|
||||
"keeps valid empty or non-address answers complete: %j",
|
||||
async (override) => {
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture([
|
||||
{
|
||||
...cnameAndGlue,
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.cname": [],
|
||||
"dns.a": [],
|
||||
...override,
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(result.dnsQueries[0]?.response_addresses).toEqual([]);
|
||||
expect(result.partial).toBe(false);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
[[], []],
|
||||
[["", ""], []],
|
||||
@@ -64,6 +286,13 @@ it.each(["before", "after"] as const)(
|
||||
"dns.qry.name": ["api.example"],
|
||||
"dns.flags.response": ["True"],
|
||||
"dns.a": ["203.0.113.5"],
|
||||
"dns.qry.type": ["1"],
|
||||
"dns.count.queries": ["1"],
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.count.auth_rr": ["0"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": ["api.example"],
|
||||
"dns.resp.type": ["1"],
|
||||
};
|
||||
const result = await parseTsharkJsonOutput(
|
||||
capture(position === "before" ? [empty, response] : [response, empty]),
|
||||
@@ -112,11 +341,25 @@ it("binds interleaved DNS answers to the question in their response packet", asy
|
||||
"dns.qry.name": ["alpha.example"],
|
||||
"dns.flags.response": ["True"],
|
||||
"dns.a": ["192.0.2.10"],
|
||||
"dns.qry.type": ["1"],
|
||||
"dns.count.queries": ["1"],
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.count.auth_rr": ["0"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": ["alpha.example"],
|
||||
"dns.resp.type": ["1"],
|
||||
},
|
||||
{
|
||||
"dns.qry.name": ["beta.example"],
|
||||
"dns.flags.response": ["True"],
|
||||
"dns.a": ["192.0.2.20"],
|
||||
"dns.qry.type": ["1"],
|
||||
"dns.count.queries": ["1"],
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.count.auth_rr": ["0"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": ["beta.example"],
|
||||
"dns.resp.type": ["1"],
|
||||
},
|
||||
]),
|
||||
);
|
||||
@@ -197,6 +440,13 @@ it("preserves all DNS addresses and their exact examined count", async () => {
|
||||
"dns.qry.name": ["rotating.example"],
|
||||
"dns.flags.response": ["True"],
|
||||
"dns.a": [`192.0.2.${String(index)}`],
|
||||
"dns.qry.type": ["1"],
|
||||
"dns.count.queries": ["1"],
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.count.auth_rr": ["0"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": ["rotating.example"],
|
||||
"dns.resp.type": ["1"],
|
||||
})),
|
||||
),
|
||||
);
|
||||
|
||||
+11
-15
@@ -6,6 +6,7 @@ import {
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { parseRecordedTransportUrl } from "../domain/recordedTransportUrl.js";
|
||||
import { fixedAllowanceOptions } from "../domain/fixedAllowanceRemediation.js";
|
||||
import { attributeDnsAddresses } from "./PcapDnsOutput.js";
|
||||
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
|
||||
|
||||
const OPERATION = "inspect_pcap_capture";
|
||||
@@ -222,6 +223,7 @@ export const parseTsharkJsonOutput = async (
|
||||
let unresolvedHttpUriTarget = false;
|
||||
const unattributed = new Set<string>();
|
||||
let unattributedPackets = 0;
|
||||
let unresolvedDnsOwnership = false;
|
||||
let retainedBytes = 0;
|
||||
const charge = (value: string): void => {
|
||||
retainedBytes += 192 + 2 * Buffer.byteLength(value);
|
||||
@@ -304,21 +306,15 @@ export const parseTsharkJsonOutput = async (
|
||||
charge(question);
|
||||
dns.set(question, new Set());
|
||||
}
|
||||
const addresses = [...field("dns.a"), ...field("dns.aaaa")].filter(
|
||||
(value) => value !== "",
|
||||
);
|
||||
const response = field("dns.flags.response");
|
||||
if (addresses.length > 0) {
|
||||
if (
|
||||
questions.size === 1 &&
|
||||
response.length > 0 &&
|
||||
response.every((value) => value === "True" || value === "1")
|
||||
) {
|
||||
const values = dns.get(questions.values().next().value!)!;
|
||||
for (const address of addresses) add(values, address);
|
||||
} else {
|
||||
{
|
||||
const attribution = attributeDnsAddresses(field);
|
||||
unresolvedDnsOwnership ||= attribution.incomplete;
|
||||
for (const [question, address] of attribution.assigned)
|
||||
add(dns.get(question)!, address);
|
||||
if (attribution.unattributed.length > 0) {
|
||||
unattributedPackets += 1;
|
||||
for (const address of addresses) add(unattributed, address);
|
||||
for (const address of attribution.unattributed)
|
||||
add(unattributed, address);
|
||||
}
|
||||
}
|
||||
for (const value of field("tls.handshake.extensions_server_name"))
|
||||
@@ -422,7 +418,7 @@ export const parseTsharkJsonOutput = async (
|
||||
packetCount: packets.length,
|
||||
partial:
|
||||
observedUris.size > uris.size ||
|
||||
unattributedPackets > 0 ||
|
||||
unresolvedDnsOwnership ||
|
||||
unresolvedHttpUriTarget,
|
||||
};
|
||||
};
|
||||
@@ -75,6 +75,14 @@ const FIELDS = [
|
||||
"ipv6.dst",
|
||||
"dns.flags.response",
|
||||
"dns.qry.name",
|
||||
"dns.qry.type",
|
||||
"dns.count.queries",
|
||||
"dns.count.answers",
|
||||
"dns.count.auth_rr",
|
||||
"dns.count.add_rr",
|
||||
"dns.resp.name",
|
||||
"dns.resp.type",
|
||||
"dns.cname",
|
||||
"dns.a",
|
||||
"dns.aaaa",
|
||||
"tls.handshake.extensions_server_name",
|
||||
@@ -1059,7 +1067,7 @@ export class PcapProvider {
|
||||
limitations: [
|
||||
...this.nativeLimitations,
|
||||
"Both tools read one owned snapshot; target identity comes from its admitted source descriptor",
|
||||
"DNS address values are associated only with a single question in the same response packet; values may include additional DNS records, not only final resolution answers",
|
||||
"DNS address attribution requires consistent RR owner/type and section counts for one question in one response; only matching answer owners or unambiguous answer-section CNAME chains are associated. Authority, additional, unrelated and ambiguous addresses are retained separately as unattributed; these packet-local relationships do not prove final resolution",
|
||||
"Only bytes present in the saved capture are examined; encryption and capture truncation can hide higher-level fields",
|
||||
"HTTP URI inventory examines decoded HTTP/1 and HTTP/2 request fields; HTTP/3 URI semantics are outside this profile",
|
||||
...(fields.partial
|
||||
|
||||
@@ -29,6 +29,13 @@ const createPcapToolFixture = async (root: string) => {
|
||||
"dns.qry.name": ["many.example"],
|
||||
"dns.flags.response": ["True"],
|
||||
"dns.a": [`198.51.100.${index}`],
|
||||
"dns.qry.type": ["1"],
|
||||
"dns.count.queries": ["1"],
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.count.auth_rr": ["0"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": ["many.example"],
|
||||
"dns.resp.type": ["1"],
|
||||
"tls.handshake.extensions_server_name": [`host${index}.example`],
|
||||
"http.request.full_uri": [
|
||||
"http://user:[email protected]/p?q=keep",
|
||||
|
||||
Vendored
+7
@@ -24,6 +24,13 @@ export const TSHARK_JSON = JSON.stringify([
|
||||
"dns.qry.name": ["api.example.com"],
|
||||
"dns.flags.response": ["1"],
|
||||
"dns.a": ["93.184.216.34"],
|
||||
"dns.qry.type": ["1"],
|
||||
"dns.count.queries": ["1"],
|
||||
"dns.count.answers": ["1"],
|
||||
"dns.count.auth_rr": ["0"],
|
||||
"dns.count.add_rr": ["0"],
|
||||
"dns.resp.name": ["api.example.com"],
|
||||
"dns.resp.type": ["1"],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user