@slicedearth/whoisleuth-cli 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISCLOSURE +1 -1
- package/NOTICE +2 -2
- package/cli/argument-values.mjs +55 -0
- package/cli/arguments.mjs +24 -470
- package/cli/artifact-validation/case-response.mjs +67 -9
- package/cli/artifact-validation/lookup-evidence.mjs +18 -4
- package/cli/artifact-validation/signed-review.mjs +61 -64
- package/cli/artifact-verify.mjs +11 -2
- package/cli/assurance-arguments.mjs +56 -0
- package/cli/assurance-command-definitions.mjs +179 -0
- package/cli/assurance-command-runner.mjs +1 -1
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +1 -1
- package/cli/bulk-command-runner.mjs +6 -8
- package/cli/bulk.mjs +2 -3
- package/cli/case-command.mjs +99 -6
- package/cli/case-pack.mjs +19 -4
- package/cli/ci-report.mjs +6 -3
- package/cli/collection-arguments.mjs +123 -0
- package/cli/collection-command-definitions.mjs +330 -0
- package/cli/collection-preflight.mjs +8 -1
- package/cli/command-argument-grammar.mjs +8 -210
- package/cli/command-definition.mjs +330 -0
- package/cli/command-reference.mjs +55 -1388
- package/cli/command-seeds.mjs +18 -0
- package/cli/compare.mjs +1 -1
- package/cli/config-profile.mjs +1 -1
- package/cli/ct-event-intake.mjs +4 -4
- package/cli/discover.mjs +2 -2
- package/cli/discovery-observation-snapshot.mjs +2 -2
- package/cli/discovery-scan-command-runner.mjs +7 -9
- package/cli/discovery-scan.mjs +1 -1
- package/cli/discovery-snapshot.mjs +2 -2
- package/cli/discovery-workflow.mjs +4 -4
- package/cli/domain-control-monitor.mjs +6 -6
- package/cli/domain-control-observations.mjs +2 -1
- package/cli/domain-feed.mjs +61 -0
- package/cli/errors.mjs +5 -6
- package/cli/evidence-arguments.mjs +46 -0
- package/cli/evidence-command-definitions.mjs +69 -0
- package/cli/evidence-command-runner.mjs +2 -3
- package/cli/evidence-signing.mjs +3 -2
- package/cli/external-observation-mapping.mjs +2 -2
- package/cli/formatters/json.mjs +1 -13
- package/cli/formatters/terminal-command-formats.mjs +6 -0
- package/cli/formatters/terminal-lookup.mjs +6 -0
- package/cli/history-arguments.mjs +39 -0
- package/cli/history-command-definitions.mjs +88 -0
- package/cli/indicator-set-command.mjs +32 -0
- package/cli/inline-command-families.mjs +6 -47
- package/cli/inline-command-runner.mjs +4 -31
- package/cli/input.mjs +2 -2
- package/cli/intake-command.mjs +147 -0
- package/cli/interchange-report.mjs +4 -2
- package/cli/investigation-artifacts.mjs +3 -4
- package/cli/investigation-package-review.mjs +2 -0
- package/cli/investigation-plan.mjs +32 -154
- package/cli/investigation-recipes.mjs +177 -0
- package/cli/investigation-run.mjs +78 -12
- package/cli/local-mmdb-review.mjs +220 -17
- package/cli/lookalike-calibration.mjs +1 -1
- package/cli/lookup-brief.mjs +2 -2
- package/cli/lookup-command-runner.mjs +19 -35
- package/cli/lookup-diff.mjs +10 -9
- package/cli/lookup-plan.mjs +10 -14
- package/cli/lookup-reconcile.mjs +6 -5
- package/cli/lookup-timeline.mjs +7 -6
- package/cli/mail-header-review.mjs +24 -247
- package/cli/mail-review.mjs +9 -7
- package/cli/network-arguments.mjs +51 -0
- package/cli/network-command-definitions.mjs +130 -0
- package/cli/network-command-runner.mjs +43 -31
- package/cli/offline-evidence-review.mjs +85 -6
- package/cli/posture.mjs +1 -1
- package/cli/registry-fixture-scaffold.mjs +1 -1
- package/cli/retained-artifact-diff.mjs +8 -7
- package/cli/review-arguments.mjs +132 -0
- package/cli/review-command-definitions.mjs +279 -0
- package/cli/review-command-runner.mjs +72 -159
- package/cli/risk-calibration.mjs +14 -12
- package/cli/runner.mjs +20 -32
- package/cli/saved-lookup.mjs +2 -2
- package/cli/selected-input-worker.mjs +73 -0
- package/cli/sharing-review.mjs +5 -64
- package/cli/source-reliability.mjs +2 -2
- package/cli/support-arguments.mjs +49 -0
- package/cli/support-command-definitions.mjs +209 -0
- package/cli/support-command-runner.mjs +4 -4
- package/cli/watchlist-review.mjs +43 -0
- package/cli/workflow-arguments.mjs +100 -0
- package/cli/workflow-command-definitions.mjs +103 -0
- package/cli/workflow-command-runner.mjs +40 -1
- package/docs/cli-reference.md +27 -0
- package/docs/cli.md +322 -4
- package/docs/contextual-reviews.md +178 -0
- package/docs/infrastructure-observations.md +126 -0
- package/docs/offline-intake-context.md +213 -0
- package/lib/analyst-taxonomy.mjs +2 -40
- package/lib/application-version.mjs +1 -1
- package/lib/availability.mjs +13 -789
- package/lib/bounded-contract-normalizers.mjs +4 -0
- package/lib/bounded-json.mjs +2 -330
- package/lib/browser-library-profile.mjs +62 -84
- package/lib/candidate-provenance-bounds.mjs +2 -0
- package/lib/classify.mjs +1 -1
- package/lib/credential-form-attribution.mjs +136 -0
- package/lib/credential-surface-profile.mjs +6 -2
- package/lib/ct-query.mjs +2 -27
- package/lib/ct-response-bounds.mjs +2 -9
- package/lib/ct-search.mjs +83 -47
- package/lib/dmarc-discovery.mjs +62 -0
- package/lib/dns-convergence-review.mjs +21 -8
- package/lib/dns-delegation-health.mjs +35 -15
- package/lib/dns-inheritance-review.mjs +53 -63
- package/lib/dns-intelligence.mjs +27 -11
- package/lib/dns-query-coverage.mjs +40 -0
- package/lib/domain-assurance.mjs +1 -1
- package/lib/domain-change-packet.mjs +12 -13
- package/lib/domain-change-review.mjs +32 -14
- package/lib/domain-control-flight-recorder.mjs +103 -27
- package/lib/domain-control-manifest.mjs +25 -17
- package/lib/domain-posture-analysis.mjs +75 -7
- package/lib/domain-posture-parsers.mjs +9 -0
- package/lib/domain-posture.mjs +45 -25
- package/lib/domain-registration.mjs +392 -0
- package/lib/evidence-export-privacy.mjs +27 -17
- package/lib/evidence-export.mjs +3 -3
- package/lib/evidence-report.mjs +5 -1
- package/lib/external-intelligence-risk.mjs +2 -124
- package/lib/favicon.mjs +13 -4
- package/lib/generated/cisa-kev-catalog.mjs +41 -3
- package/lib/generated/registrar-standing-catalogue.mjs +18 -10
- package/lib/generated/retire-browser-catalog.mjs +332 -19
- package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
- package/lib/html-signals.mjs +13 -2
- package/lib/http-evidence-bounds.mjs +2 -8
- package/lib/http-intelligence.mjs +13 -1
- package/lib/idn-confusable-policy.mjs +6 -4
- package/lib/idn-confusables.mjs +1 -1
- package/lib/interchange-fidelity-registry.mjs +10 -0
- package/lib/lookup-child-profile-contract.mjs +28 -11
- package/lib/lookup-contract-primitives.mjs +1 -1
- package/lib/lookup-diagnostics.mjs +1 -23
- package/lib/lookup-network-evidence-bounds.mjs +1 -10
- package/lib/lookup-progress.mjs +50 -1
- package/lib/lookup-response-contract.mjs +70 -40
- package/lib/lookup-response.mjs +5 -15
- package/lib/lookup-source-progress.mjs +8 -22
- package/lib/lookup.mjs +1 -1
- package/lib/operation-budget.mjs +4 -0
- package/lib/opportunity-scoring.mjs +2 -107
- package/lib/outbound-identity.mjs +1 -1
- package/lib/page-fingerprints.mjs +18 -1
- package/lib/perceptual-hash-comparison.mjs +2 -34
- package/lib/perceptual-hash.mjs +36 -10
- package/lib/portable-generator.mjs +2 -29
- package/lib/project-metadata.mjs +2 -8
- package/lib/rdap-client.mjs +11 -7
- package/lib/registrable-domain.mjs +2 -56
- package/lib/registrar-standing-contract.mjs +1 -1
- package/lib/registrar-standing.mjs +9 -13
- package/lib/registry-capabilities.mjs +2 -2
- package/lib/registry-capability-catalogue.mjs +27 -19
- package/lib/risk-calibration-summary.mjs +2 -352
- package/lib/risk-scoring.mjs +2 -345
- package/lib/scoring-evidence-quality.mjs +2 -107
- package/lib/semantic-version.mjs +2 -54
- package/lib/service-binding-dns.mjs +46 -2
- package/lib/smtp-transport-review.mjs +48 -19
- package/lib/sslbl-certificates.generated.mjs +142 -139
- package/lib/static-html-analysis.mjs +32 -4
- package/lib/structured-data-identity.mjs +1 -1
- package/lib/technology-evidence-role.mjs +1 -51
- package/lib/threat-intelligence-manifests.mjs +1 -1
- package/lib/threat-intelligence-runtime.mjs +1 -1
- package/lib/threat-intelligence-targets.mjs +1 -1
- package/lib/threat-intelligence-types.mjs +2 -75
- package/lib/tls-intelligence.mjs +30 -5
- package/lib/typosquat-generator.mjs +11 -2
- package/lib/website-enrichment.mjs +404 -0
- package/lib/whois-authority.mjs +43 -5
- package/lib/whois-chain.mjs +12 -3
- package/lib/whois-common-formats.mjs +3 -1
- package/lib/whois-contacts.mjs +13 -2
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-parser.mjs +5 -3
- package/package.json +15 -14
- package/packages/analysis/analyst-taxonomy.mjs +40 -0
- package/packages/analysis/bounded-json.mjs +330 -0
- package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
- package/packages/analysis/cli-argument-grammar.mjs +216 -0
- package/packages/analysis/ct-query.mjs +27 -0
- package/packages/analysis/ct-response-bounds.mjs +9 -0
- package/packages/analysis/external-intelligence-risk.mjs +124 -0
- package/packages/analysis/http-evidence-bounds.mjs +8 -0
- package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
- package/packages/analysis/opportunity-scoring.mjs +107 -0
- package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
- package/packages/analysis/portable-generator.mjs +29 -0
- package/packages/analysis/project-metadata.mjs +8 -0
- package/packages/analysis/registrable-domain.mjs +56 -0
- package/packages/analysis/risk-calibration-summary.mjs +352 -0
- package/packages/analysis/risk-scoring.mjs +348 -0
- package/packages/analysis/scoring-evidence-quality.mjs +107 -0
- package/packages/analysis/semantic-version.mjs +54 -0
- package/packages/analysis/threat-intelligence-types.mjs +75 -0
- package/packages/cases/case-containment-handoff.mjs +195 -0
- package/packages/cases/case-evidence-links.mjs +151 -0
- package/packages/cases/case-evidence-model.mjs +89 -12
- package/packages/cases/case-evidence-quality.mjs +5 -0
- package/packages/cases/case-export-input.mjs +7 -5
- package/packages/cases/case-incident-context.mjs +52 -0
- package/packages/cases/case-markdown.mjs +11 -0
- package/packages/cases/case-migration-model.mjs +40 -24
- package/packages/cases/case-object-coverage.mjs +33 -0
- package/packages/cases/case-packet-correction.mjs +106 -0
- package/packages/cases/case-recheck-model.mjs +124 -36
- package/packages/cases/case-record-contracts.mjs +1 -1
- package/packages/cases/case-record-core.mjs +1 -1
- package/packages/cases/case-record-operations.mjs +111 -118
- package/packages/cases/case-record-projection.mjs +23 -4
- package/packages/cases/case-record-version-input.mjs +41 -0
- package/packages/cases/case-report.mjs +93 -27
- package/packages/cases/case-requested-evidence.mjs +146 -0
- package/packages/cases/case-response-actions.mjs +159 -1
- package/packages/cases/case-response-model.mjs +16 -1
- package/packages/cases/case-response-object.mjs +119 -0
- package/packages/cases/case-response-outcomes.mjs +108 -20
- package/packages/cases/case-response-packet-render.mjs +35 -12
- package/packages/cases/case-response-packet.mjs +55 -13
- package/packages/cases/case-response-records.mjs +0 -1
- package/packages/cases/case-response-review-inputs.mjs +60 -8
- package/packages/cases/case-storage-model.mjs +1 -1
- package/packages/cases/case-workflow-metadata.mjs +217 -0
- package/packages/cases/case-workflow-migration.mjs +43 -0
- package/packages/cases/http-summary.mjs +6 -1
- package/packages/comparison/favicon-similarity.mjs +1 -1
- package/packages/comparison/page-similarity.mjs +1 -1
- package/packages/comparison/relationship-evidence.mjs +21 -9
- package/packages/comparison/relationship-provenance.mjs +7 -1
- package/packages/contracts/analyst-interchange.mjs +35 -15
- package/packages/contracts/candidate-watch-review.mjs +54 -0
- package/packages/contracts/case-portability.mjs +360 -49
- package/packages/contracts/cli-command-semantics.mjs +5 -0
- package/packages/contracts/cli-grammar.mjs +6 -0
- package/packages/contracts/cli-lookup-brief.mjs +2 -0
- package/packages/contracts/context-review.mjs +25 -0
- package/packages/contracts/document-intake.mjs +11 -0
- package/packages/contracts/domain-control-flight-recorder.mjs +30 -14
- package/packages/contracts/domain-control-manifest.mjs +1 -1
- package/packages/contracts/domain-control-monitor.mjs +13 -2
- package/packages/contracts/domain-control-review.mjs +36 -15
- package/packages/contracts/external-observation-interchange.mjs +96 -28
- package/packages/contracts/har-review.mjs +2 -0
- package/packages/contracts/identity-events.mjs +4 -0
- package/packages/contracts/investigation-run.mjs +2 -0
- package/packages/contracts/ip-address.mjs +10 -0
- package/packages/contracts/lookup-evidence.mjs +3 -2
- package/packages/contracts/mail-authentication.mjs +5 -0
- package/packages/contracts/message-intake.mjs +37 -0
- package/packages/contracts/offline-comparison.mjs +4 -1
- package/packages/contracts/passive-mail.mjs +10 -0
- package/packages/contracts/risk-calibration.mjs +44 -29
- package/packages/contracts/schema-lifecycle.mjs +3 -1
- package/packages/contracts/web-capture.mjs +2 -2
- package/packages/contracts/workspace-portability.mjs +144 -28
- package/packages/evidence/collection-quality.mjs +67 -0
- package/packages/evidence/credential-form-attribution.mjs +60 -0
- package/packages/evidence/dns-query-outcome.mjs +72 -0
- package/packages/evidence/email-recipient.mjs +18 -0
- package/packages/evidence/image-regions.mjs +15 -0
- package/packages/evidence/lookup-target.mjs +5 -1
- package/packages/evidence/observation.mjs +2 -0
- package/packages/evidence/png-pixels.mjs +117 -0
- package/packages/evidence/public-address-policy.mjs +83 -0
- package/packages/evidence/record-identity.mjs +60 -16
- package/packages/evidence/technology-evidence-role.mjs +51 -0
- package/packages/interchange/defensive-indicator-export.mjs +264 -0
- package/packages/interchange/external-findings-import.mjs +36 -29
- package/packages/interchange/json-output.mjs +14 -0
- package/packages/interchange/managed-indicator-export.mjs +36 -0
- package/packages/interchange/managed-indicator-set.mjs +223 -0
- package/packages/interchange/misp-indicator-export.mjs +93 -0
- package/packages/interchange/sharing-policy.mjs +56 -0
- package/packages/interchange/stix-indicator-export.mjs +129 -0
- package/packages/interchange/web-capture-import.mjs +22 -15
- package/packages/investigation/acquisition-decision-packet.mjs +79 -1
- package/packages/investigation/bulk-mail-exposure.mjs +7 -13
- package/packages/investigation/capture-coverage.mjs +57 -0
- package/packages/investigation/connector-provenance-review.mjs +100 -0
- package/packages/investigation/context-review.mjs +35 -0
- package/packages/investigation/document-intake.mjs +87 -0
- package/packages/investigation/docx-intake.mjs +133 -0
- package/packages/investigation/domain-history-review.mjs +132 -0
- package/packages/investigation/domain-transition-review.mjs +49 -0
- package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
- package/packages/investigation/har-intake.mjs +68 -0
- package/packages/investigation/identity-event-intake.mjs +131 -0
- package/packages/investigation/identity-incident-review.mjs +39 -0
- package/packages/investigation/incident-sequence-review.mjs +58 -0
- package/packages/investigation/infrastructure-observation.mjs +234 -0
- package/packages/investigation/intake-context.mjs +284 -0
- package/packages/investigation/intake-indicators.mjs +116 -0
- package/packages/investigation/intake-phones.mjs +155 -0
- package/packages/investigation/intake-report.mjs +20 -0
- package/packages/investigation/investigation-manifest.mjs +35 -9
- package/packages/investigation/link-intake.mjs +209 -0
- package/packages/investigation/lookup-asset-graph.mjs +1 -1
- package/packages/investigation/mail-authentication-review.mjs +222 -0
- package/packages/investigation/mail-header-identity.mjs +209 -0
- package/packages/investigation/message-intake.mjs +235 -0
- package/packages/investigation/page-behaviour.mjs +113 -0
- package/packages/investigation/pdf-intake.mjs +155 -0
- package/packages/investigation/platform-continuity-review.mjs +60 -0
- package/packages/investigation/qr-intake.mjs +56 -0
- package/packages/investigation/requested-action-hints.mjs +11 -0
- package/packages/investigation/selected-input-review.mjs +17 -0
- package/packages/investigation/storefront-review.mjs +70 -0
- package/packages/monitoring/domain-feed.mjs +321 -0
- package/packages/workspace/brand-candidate-workflow.mjs +351 -0
- package/packages/workspace/brand-keyword-campaign.mjs +231 -0
- package/packages/workspace/brand-profile-model.mjs +48 -4
- package/packages/workspace/bulk-review-model.mjs +28 -26
- package/packages/workspace/bulk-session-model.mjs +27 -13
- package/packages/workspace/campaign-model.mjs +1 -1
- package/packages/workspace/candidate-watch-handoff.mjs +195 -0
- package/packages/workspace/detection-rule-model.mjs +11 -3
- package/packages/workspace/hostile-input.mjs +1 -1
- package/packages/workspace/investigation-guide.mjs +2 -2
- package/packages/workspace/investigation-template-model.mjs +26 -2
- package/packages/workspace/page-baseline.mjs +2 -2
- package/packages/workspace/relationship-observation-model.mjs +17 -13
- package/packages/workspace/shortlist-model.mjs +24 -7
- package/packages/workspace/watchlist-history.mjs +59 -16
- package/packages/workspace/watchlist-store.mjs +152 -11
- package/packages/workspace/website-snapshot-model.mjs +28 -3
- package/packages/workspace/workspace-archive-crypto.mjs +1 -1
- package/packages/workspace/workspace-archive.mjs +5 -4
- package/third-party-notices.txt +549 -16
- package/lib/hostname.mjs +0 -2
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { htmlTreeEvents, isHtmlElement, parseBoundedHtml, MAX_STATIC_HTML_CHARS, MAX_STATIC_HTML_TAGS, MAX_STATIC_HTML_TREE_EVENTS, } from "./bounded-html-document.mjs";
|
|
5
5
|
import { MAX_CSP_META_POLICIES, MAX_RESPONSE_POLICY_HEADER_BYTES, } from "./response-policy.mjs";
|
|
6
6
|
import { MAX_FAVICON_BYTES, MAX_FAVICON_CANDIDATES } from "./outbound-request-bounds.mjs";
|
|
7
|
+
import { CREDENTIAL_CATEGORY_NAMES, MAX_CREDENTIAL_FORMS, MAX_CREDENTIAL_INPUTS } from "../packages/evidence/credential-form-attribution.mjs";
|
|
7
8
|
import { MAX_PAGE_PUBLICATION_DECLARATIONS, MAX_PAGE_PUBLICATION_META_ELEMENTS, MAX_PAGE_PUBLICATION_ROBOTS_DIRECTIVES, } from "./homepage-metadata-contract.mjs";
|
|
8
9
|
const MAX_STATIC_STRUCTURE_TOKENS = MAX_STATIC_HTML_TREE_EVENTS;
|
|
9
10
|
const MAX_TAG_LENGTH = 4_096;
|
|
@@ -14,8 +15,8 @@ const MAX_SCRIPT_MEDIA_TYPE_LENGTH = 120;
|
|
|
14
15
|
const MAX_INLINE_SCRIPT_TOTAL_CHARS = 65_536;
|
|
15
16
|
const MAX_INLINE_SCRIPT_CHARS = MAX_INLINE_SCRIPT_TOTAL_CHARS;
|
|
16
17
|
const MAX_STATIC_VISIBLE_TEXT_CHARS = MAX_STATIC_HTML_CHARS;
|
|
17
|
-
const MAX_STATIC_FORMS =
|
|
18
|
-
const MAX_STATIC_INPUTS =
|
|
18
|
+
const MAX_STATIC_FORMS = MAX_CREDENTIAL_FORMS;
|
|
19
|
+
const MAX_STATIC_INPUTS = MAX_CREDENTIAL_INPUTS;
|
|
19
20
|
const MAX_STATIC_PUBLICATION_META_ELEMENTS = MAX_PAGE_PUBLICATION_META_ELEMENTS;
|
|
20
21
|
const MAX_STATIC_PUBLICATION_DECLARATIONS = MAX_PAGE_PUBLICATION_DECLARATIONS;
|
|
21
22
|
const MAX_STATIC_ROBOTS_DIRECTIVES = MAX_PAGE_PUBLICATION_ROBOTS_DIRECTIVES;
|
|
@@ -197,6 +198,29 @@ function stylesheetCandidate(attributes) {
|
|
|
197
198
|
truncated: rel.truncated || href.truncated || disabled.truncated || media.truncated,
|
|
198
199
|
};
|
|
199
200
|
}
|
|
201
|
+
export function staticControlDisabled(elements, index, firstLegends) {
|
|
202
|
+
const hasDisabled = (element) => element.attributes.some(attribute => attribute.name === 'disabled');
|
|
203
|
+
if (hasDisabled(elements[index]))
|
|
204
|
+
return true;
|
|
205
|
+
for (let parent = elements[index]?.parent ?? null; parent !== null; parent = elements[parent]?.parent ?? null) {
|
|
206
|
+
const fieldset = elements[parent];
|
|
207
|
+
if (!fieldset.html || fieldset.name !== 'fieldset' || !hasDisabled(fieldset))
|
|
208
|
+
continue;
|
|
209
|
+
const legend = firstLegends.get(parent);
|
|
210
|
+
let underLegend = false;
|
|
211
|
+
for (let current = index; current !== null; current = elements[current]?.parent ?? null) {
|
|
212
|
+
if (current === legend) {
|
|
213
|
+
underLegend = true;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
if (current === parent)
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
if (!underLegend)
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
200
224
|
function analyzeStaticHtml(value, options = {}) {
|
|
201
225
|
const parsed = parseBoundedHtml(value);
|
|
202
226
|
const { inputLimitReached } = parsed;
|
|
@@ -209,6 +233,7 @@ function analyzeStaticHtml(value, options = {}) {
|
|
|
209
233
|
const iconCounts = [0, 0];
|
|
210
234
|
const tokens = [];
|
|
211
235
|
const elementStack = [];
|
|
236
|
+
const firstLegends = new Map();
|
|
212
237
|
const visibleTextParts = [];
|
|
213
238
|
const structureTokens = [];
|
|
214
239
|
const scripts = [];
|
|
@@ -386,6 +411,8 @@ function analyzeStaticHtml(value, options = {}) {
|
|
|
386
411
|
};
|
|
387
412
|
elementStack.push(elements.length);
|
|
388
413
|
elements.push(element);
|
|
414
|
+
if (htmlElement && tagName === 'legend' && element.parent !== null && !firstLegends.has(element.parent))
|
|
415
|
+
firstLegends.set(element.parent, elements.length - 1);
|
|
389
416
|
appendToken({ kind: 'start', element });
|
|
390
417
|
// Foreign drawing attributes are not HTML technology, form or resource
|
|
391
418
|
// inputs. Their per-element omission still reaches fingerprinting, but
|
|
@@ -628,7 +655,8 @@ function analyzeStaticHtml(value, options = {}) {
|
|
|
628
655
|
}
|
|
629
656
|
else {
|
|
630
657
|
forms.inputsObserved += 1;
|
|
631
|
-
const categories =
|
|
658
|
+
const categories = staticControlDisabled(elements, elements.length - 1, firstLegends)
|
|
659
|
+
? { values: [], truncated: false } : inputCategories(token.attrs);
|
|
632
660
|
if (categories.truncated)
|
|
633
661
|
forms.truncated = true;
|
|
634
662
|
if (categories.values.length)
|
|
@@ -716,4 +744,4 @@ function analyzeStaticHtml(value, options = {}) {
|
|
|
716
744
|
inlineCharactersExamined,
|
|
717
745
|
};
|
|
718
746
|
}
|
|
719
|
-
export { MAX_ATTRIBUTES_PER_TAG, MAX_INLINE_SCRIPT_CHARS, MAX_INLINE_SCRIPT_TOTAL_CHARS, MAX_SCRIPT_ELEMENTS, MAX_SCRIPT_MEDIA_TYPE_LENGTH, MAX_STATIC_FORMS, MAX_STATIC_HTML_CHARS, MAX_STATIC_INPUTS, MAX_STATIC_PUBLICATION_META_ELEMENTS, MAX_STATIC_PUBLICATION_DECLARATIONS, MAX_STATIC_ROBOTS_DIRECTIVES, MAX_STATIC_HTML_TAGS, MAX_STATIC_STRUCTURE_TOKENS, MAX_STATIC_VISIBLE_TEXT_CHARS, MAX_TAG_LENGTH, analyzeStaticHtml, };
|
|
747
|
+
export { MAX_ATTRIBUTES_PER_TAG, MAX_INLINE_SCRIPT_CHARS, MAX_INLINE_SCRIPT_TOTAL_CHARS, MAX_SCRIPT_ELEMENTS, MAX_SCRIPT_REFERENCE_LENGTH, MAX_SCRIPT_MEDIA_TYPE_LENGTH, MAX_STATIC_FORMS, MAX_STATIC_HTML_CHARS, MAX_STATIC_INPUTS, MAX_STATIC_PUBLICATION_META_ELEMENTS, MAX_STATIC_PUBLICATION_DECLARATIONS, MAX_STATIC_ROBOTS_DIRECTIVES, MAX_STATIC_HTML_TAGS, MAX_STATIC_STRUCTURE_TOKENS, MAX_STATIC_VISIBLE_TEXT_CHARS, MAX_TAG_LENGTH, analyzeStaticHtml, inputCategories as classifyStaticCredentialInput, };
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { domainToASCII } from 'node:url';
|
|
7
7
|
import { createObservation } from "../packages/evidence/observation.mjs";
|
|
8
8
|
import { MAX_STRUCTURED_DATA_ENTITIES, MAX_STRUCTURED_DATA_SAME_AS_HOSTS, STRUCTURED_DATA_IDENTITY_VERSION, } from "./lookup-child-profile-contract.mjs";
|
|
9
|
-
import { isUriShapedLabel } from "
|
|
9
|
+
import { isUriShapedLabel } from "../packages/analysis/portable-generator.mjs";
|
|
10
10
|
import { analyzeStaticHtml, } from "./static-html-analysis.mjs";
|
|
11
11
|
const MAX_STRUCTURED_DATA_SCRIPTS = 8;
|
|
12
12
|
const MAX_STRUCTURED_DATA_SCRIPT_CHARS = 16_384;
|
|
@@ -1,51 +1 @@
|
|
|
1
|
-
|
|
2
|
-
'observed_edge',
|
|
3
|
-
'application_platform',
|
|
4
|
-
'framework_runtime',
|
|
5
|
-
'embedded_dependency',
|
|
6
|
-
]);
|
|
7
|
-
const TECHNOLOGY_EVIDENCE_ROLE_SET = new Set(TECHNOLOGY_EVIDENCE_ROLE_ORDER);
|
|
8
|
-
function record(value) {
|
|
9
|
-
return value && typeof value === 'object' && !Array.isArray(value)
|
|
10
|
-
? value
|
|
11
|
-
: {};
|
|
12
|
-
}
|
|
13
|
-
function technologyEvidenceRoles(value) {
|
|
14
|
-
const finding = record(value);
|
|
15
|
-
const roles = new Set();
|
|
16
|
-
let hasExplicitRole = false;
|
|
17
|
-
for (const role of Array.isArray(finding.roles) ? finding.roles.slice(0, 4) : []) {
|
|
18
|
-
if (typeof role === 'string' && TECHNOLOGY_EVIDENCE_ROLE_SET.has(role)) {
|
|
19
|
-
roles.add(role);
|
|
20
|
-
hasExplicitRole = true;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
const evidenceValues = Array.isArray(finding.evidence) ? finding.evidence.slice(0, 4) : [];
|
|
24
|
-
for (const evidenceValue of evidenceValues) {
|
|
25
|
-
const evidence = record(evidenceValue);
|
|
26
|
-
if (typeof evidence.role === 'string'
|
|
27
|
-
&& TECHNOLOGY_EVIDENCE_ROLE_SET.has(evidence.role)) {
|
|
28
|
-
roles.add(evidence.role);
|
|
29
|
-
hasExplicitRole = true;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (!hasExplicitRole) {
|
|
33
|
-
const resourceOriginOnly = evidenceValues.length > 0
|
|
34
|
-
&& evidenceValues.every((evidence) => (String(record(evidence).source).toLowerCase() === 'resource origin'));
|
|
35
|
-
if (evidenceValues.some((evidence) => String(record(evidence).source).toLowerCase() === 'resource origin')) {
|
|
36
|
-
roles.add('embedded_dependency');
|
|
37
|
-
}
|
|
38
|
-
if (!resourceOriginOnly) {
|
|
39
|
-
const category = String(finding.category).toLowerCase();
|
|
40
|
-
if (category === 'delivery platform')
|
|
41
|
-
roles.add('observed_edge');
|
|
42
|
-
else if (['application runtime', 'web framework', 'static site generator', 'web server'].includes(category)) {
|
|
43
|
-
roles.add('framework_runtime');
|
|
44
|
-
}
|
|
45
|
-
else
|
|
46
|
-
roles.add('application_platform');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return TECHNOLOGY_EVIDENCE_ROLE_ORDER.filter((role) => roles.has(role));
|
|
50
|
-
}
|
|
51
|
-
export { TECHNOLOGY_EVIDENCE_ROLE_ORDER, technologyEvidenceRoles, };
|
|
1
|
+
export { TECHNOLOGY_EVIDENCE_ROLE_ORDER, technologyEvidenceRoles, } from "../packages/evidence/technology-evidence-role.mjs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { registerCuratedConnector, registerThreatIntelligenceProvider, isCuratedConnectorDefinition, isThreatIntelligenceProvider, } from "./threat-intelligence-definition-registry.mjs";
|
|
2
2
|
import { CURATED_CONNECTOR_CONTRACT_VERSION, MAX_CACHE_TTL_MS, MAX_CONNECTOR_ENTITIES, MAX_CONNECTOR_RELATIONSHIPS, MAX_RESPONSE_BYTES, MAX_TIMEOUT_MS, THREAT_INTELLIGENCE_CONTRACT_VERSION, } from "./threat-intelligence-runtime.mjs";
|
|
3
3
|
import { boundedInteger, boundedHttpsUrl, enumValue, exactKeys, isRecord, strictBoundedString, } from "./bounded-contract-normalizers.mjs";
|
|
4
|
-
import { THREAT_INTELLIGENCE_CAPABILITIES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, CURATED_CONNECTOR_KINDS, CURATED_CONNECTOR_COLLECTIONS, CURATED_CONNECTOR_CREDENTIAL_MODES, CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_TARGET_EXPOSURES, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, } from "
|
|
4
|
+
import { THREAT_INTELLIGENCE_CAPABILITIES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, CURATED_CONNECTOR_KINDS, CURATED_CONNECTOR_COLLECTIONS, CURATED_CONNECTOR_CREDENTIAL_MODES, CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_TARGET_EXPOSURES, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, } from "../packages/analysis/threat-intelligence-types.mjs";
|
|
5
5
|
import { normalizeLegacyIsoTimestamp } from "../packages/evidence/observation.mjs";
|
|
6
6
|
const MAX_PROVIDER_ID_LENGTH = 40;
|
|
7
7
|
const MAX_PROVIDER_LABEL_LENGTH = 100;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// output budgets before producing separately attributed normalized evidence.
|
|
6
6
|
import { createObservation } from "../packages/evidence/observation.mjs";
|
|
7
7
|
import { latestObservationCohort } from "../packages/evidence/latest-observations.mjs";
|
|
8
|
-
import { CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_RELATIONSHIP_CLASSIFICATIONS, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, THREAT_INTELLIGENCE_CONTRACT_VERSION, THREAT_INTELLIGENCE_CATEGORIES, THREAT_INTELLIGENCE_CONFIDENCES, THREAT_INTELLIGENCE_ENVELOPE_VERSION, THREAT_INTELLIGENCE_RESULT_STATES, THREAT_INTELLIGENCE_SCHEMA, THREAT_INTELLIGENCE_SEVERITIES, } from "
|
|
8
|
+
import { CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_RELATIONSHIP_CLASSIFICATIONS, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, THREAT_INTELLIGENCE_CONTRACT_VERSION, THREAT_INTELLIGENCE_CATEGORIES, THREAT_INTELLIGENCE_CONFIDENCES, THREAT_INTELLIGENCE_ENVELOPE_VERSION, THREAT_INTELLIGENCE_RESULT_STATES, THREAT_INTELLIGENCE_SCHEMA, THREAT_INTELLIGENCE_SEVERITIES, } from "../packages/analysis/threat-intelligence-types.mjs";
|
|
9
9
|
import { assertCuratedConnectorDefinition, assertThreatIntelligenceProvider, } from "./threat-intelligence-definition-registry.mjs";
|
|
10
10
|
import { boundedHttpsUrl, boundedString, isRecord, isoTimestamp, strictBoundedString, } from "./bounded-contract-normalizers.mjs";
|
|
11
11
|
import { CURATED_CONNECTOR_ENTITY_TYPES, MAX_THREAT_INTELLIGENCE_URL_LENGTH, normalizeCuratedConnectorTarget, normalizeThreatIntelligenceTarget, } from "./threat-intelligence-targets.mjs";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// and curated connectors. These helpers never perform network requests.
|
|
3
3
|
import { classifyQuery } from "./classify.mjs";
|
|
4
4
|
import { enumValue, exactKeys, strictBoundedString, } from "./bounded-contract-normalizers.mjs";
|
|
5
|
-
import { CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_TARGET_EXPOSURES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, } from "
|
|
5
|
+
import { CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_TARGET_EXPOSURES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, } from "../packages/analysis/threat-intelligence-types.mjs";
|
|
6
6
|
export const MAX_THREAT_INTELLIGENCE_URL_LENGTH = 2048;
|
|
7
7
|
const TARGET_EXPOSURES = Object.freeze({
|
|
8
8
|
domain: Object.freeze(['registrable_domain']),
|
|
@@ -1,75 +1,2 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
export const THREAT_INTELLIGENCE_SCHEMA = 'whoisleuth.threat-intelligence-result';
|
|
4
|
-
export const THREAT_INTELLIGENCE_CONTRACT_VERSION = 1;
|
|
5
|
-
export const THREAT_INTELLIGENCE_ENVELOPE_VERSION = 1;
|
|
6
|
-
export const THREAT_INTELLIGENCE_TARGET_EXPOSURES = Object.freeze([
|
|
7
|
-
'registrable_domain', 'hostname', 'origin', 'full_url',
|
|
8
|
-
]);
|
|
9
|
-
export const THREAT_INTELLIGENCE_CAPABILITIES = Object.freeze([
|
|
10
|
-
'domain_lookup', 'url_lookup', 'indicator_search',
|
|
11
|
-
]);
|
|
12
|
-
export const THREAT_INTELLIGENCE_CATEGORIES = Object.freeze([
|
|
13
|
-
'phishing',
|
|
14
|
-
'malware',
|
|
15
|
-
'spam',
|
|
16
|
-
'suspicious',
|
|
17
|
-
'abuse',
|
|
18
|
-
'unknown',
|
|
19
|
-
]);
|
|
20
|
-
export const THREAT_INTELLIGENCE_SEVERITIES = Object.freeze([
|
|
21
|
-
'critical',
|
|
22
|
-
'high',
|
|
23
|
-
'medium',
|
|
24
|
-
'low',
|
|
25
|
-
'unknown',
|
|
26
|
-
]);
|
|
27
|
-
export const THREAT_INTELLIGENCE_CONFIDENCES = Object.freeze([
|
|
28
|
-
'high',
|
|
29
|
-
'medium',
|
|
30
|
-
'low',
|
|
31
|
-
'unknown',
|
|
32
|
-
]);
|
|
33
|
-
export const THREAT_INTELLIGENCE_RESULT_STATES = Object.freeze([
|
|
34
|
-
'success',
|
|
35
|
-
'partial',
|
|
36
|
-
'not_found',
|
|
37
|
-
'unsupported',
|
|
38
|
-
'skipped',
|
|
39
|
-
'rate_limited',
|
|
40
|
-
'unavailable',
|
|
41
|
-
'error',
|
|
42
|
-
]);
|
|
43
|
-
export const CURATED_CONNECTOR_KINDS = Object.freeze(['discovery', 'enrichment']);
|
|
44
|
-
export const CURATED_CONNECTOR_COLLECTIONS = Object.freeze(['passive', 'active', 'third_party']);
|
|
45
|
-
export const CURATED_CONNECTOR_CREDENTIAL_MODES = Object.freeze(['none', 'optional', 'required']);
|
|
46
|
-
export const CURATED_CONNECTOR_ENTITY_VALUES = Object.freeze([
|
|
47
|
-
'domain', 'hostname', 'url', 'ipv4', 'ipv6', 'asn', 'certificate',
|
|
48
|
-
]);
|
|
49
|
-
export const CURATED_CONNECTOR_RELATIONSHIP_TYPES = Object.freeze([
|
|
50
|
-
'domain_resolves_to_ip', 'domain_uses_nameserver', 'domain_uses_mail_server',
|
|
51
|
-
'domain_presented_certificate', 'certificate_names_domain', 'ip_hosts_domain',
|
|
52
|
-
'domain_related_to_domain',
|
|
53
|
-
]);
|
|
54
|
-
export const CURATED_CONNECTOR_RELATIONSHIP_CLASSIFICATIONS = Object.freeze(['direct', 'normalized', 'derived']);
|
|
55
|
-
export const CURATED_CONNECTOR_TARGET_EXPOSURES = Object.freeze({
|
|
56
|
-
domain: Object.freeze(['registrable_domain']),
|
|
57
|
-
hostname: Object.freeze(['hostname']),
|
|
58
|
-
url: THREAT_INTELLIGENCE_TARGET_EXPOSURES,
|
|
59
|
-
ipv4: Object.freeze(['ip_address']),
|
|
60
|
-
ipv6: Object.freeze(['ip_address']),
|
|
61
|
-
asn: Object.freeze(['asn']),
|
|
62
|
-
certificate: Object.freeze(['certificate_fingerprint']),
|
|
63
|
-
});
|
|
64
|
-
function relationshipEndpoints(from, to) {
|
|
65
|
-
return Object.freeze({ from: Object.freeze(from), to: Object.freeze(to) });
|
|
66
|
-
}
|
|
67
|
-
export const CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS = Object.freeze({
|
|
68
|
-
domain_resolves_to_ip: relationshipEndpoints(['domain', 'hostname'], ['ipv4', 'ipv6']),
|
|
69
|
-
domain_uses_nameserver: relationshipEndpoints(['domain'], ['hostname']),
|
|
70
|
-
domain_uses_mail_server: relationshipEndpoints(['domain'], ['hostname']),
|
|
71
|
-
domain_presented_certificate: relationshipEndpoints(['domain', 'hostname'], ['certificate']),
|
|
72
|
-
certificate_names_domain: relationshipEndpoints(['certificate'], ['domain', 'hostname']),
|
|
73
|
-
ip_hosts_domain: relationshipEndpoints(['ipv4', 'ipv6'], ['domain', 'hostname']),
|
|
74
|
-
domain_related_to_domain: relationshipEndpoints(['domain', 'hostname'], ['domain', 'hostname']),
|
|
75
|
-
});
|
|
1
|
+
// Retained public import; the runtime-neutral implementation lives in the domain layer.
|
|
2
|
+
export * from "../packages/analysis/threat-intelligence-types.mjs";
|
package/lib/tls-intelligence.mjs
CHANGED
|
@@ -705,6 +705,7 @@ function skippedTlsObservation(detail = 'TLS evidence collection is disabled by
|
|
|
705
705
|
};
|
|
706
706
|
}
|
|
707
707
|
async function collectTlsIntelligence(hostname, options = {}) {
|
|
708
|
+
options.signal?.throwIfAborted();
|
|
708
709
|
const normalizedHostname = normalizeTlsHostname(hostname);
|
|
709
710
|
const now = options.now || Date.now;
|
|
710
711
|
const observedAt = options.observedAt || (() => new Date().toISOString());
|
|
@@ -725,9 +726,11 @@ async function collectTlsIntelligence(hostname, options = {}) {
|
|
|
725
726
|
resolutionDeadline = setTimer(() => reject(new Error('TLS resolution timed out')), timeoutMs);
|
|
726
727
|
}),
|
|
727
728
|
]);
|
|
729
|
+
options.signal?.throwIfAborted();
|
|
728
730
|
records = normalizePublicAddressRecords(resolved);
|
|
729
731
|
}
|
|
730
732
|
catch (error) {
|
|
733
|
+
options.signal?.throwIfAborted();
|
|
731
734
|
return failedTlsObservation(error, { sniHost: normalizedHostname, observedAt: observedAt(), durationMs: now() - started });
|
|
732
735
|
}
|
|
733
736
|
finally {
|
|
@@ -755,18 +758,29 @@ async function collectTlsIntelligence(hostname, options = {}) {
|
|
|
755
758
|
durationMs: now() - started,
|
|
756
759
|
});
|
|
757
760
|
}
|
|
758
|
-
return new Promise((resolve) => {
|
|
761
|
+
return new Promise((resolve, reject) => {
|
|
759
762
|
let socket;
|
|
760
763
|
let deadline;
|
|
761
764
|
let settled = false;
|
|
762
|
-
const
|
|
763
|
-
if (settled)
|
|
764
|
-
return;
|
|
765
|
-
settled = true;
|
|
765
|
+
const cleanup = () => {
|
|
766
766
|
if (deadline !== undefined)
|
|
767
767
|
clearTimer(deadline);
|
|
768
|
+
options.signal?.removeEventListener('abort', cancelled);
|
|
768
769
|
if (socket && typeof socket.destroy === 'function')
|
|
769
770
|
socket.destroy();
|
|
771
|
+
};
|
|
772
|
+
const cancelled = () => {
|
|
773
|
+
if (settled)
|
|
774
|
+
return;
|
|
775
|
+
settled = true;
|
|
776
|
+
cleanup();
|
|
777
|
+
reject(options.signal.reason);
|
|
778
|
+
};
|
|
779
|
+
const finish = (result) => {
|
|
780
|
+
if (settled)
|
|
781
|
+
return;
|
|
782
|
+
settled = true;
|
|
783
|
+
cleanup();
|
|
770
784
|
resolve(result);
|
|
771
785
|
};
|
|
772
786
|
const fail = (error) => finish(failedTlsObservation(error, {
|
|
@@ -776,6 +790,11 @@ async function collectTlsIntelligence(hostname, options = {}) {
|
|
|
776
790
|
observedAt: observedAt(),
|
|
777
791
|
durationMs: now() - started,
|
|
778
792
|
}));
|
|
793
|
+
options.signal?.addEventListener('abort', cancelled, { once: true });
|
|
794
|
+
if (options.signal?.aborted) {
|
|
795
|
+
cancelled();
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
779
798
|
try {
|
|
780
799
|
socket = connect({
|
|
781
800
|
host: selected.address,
|
|
@@ -787,6 +806,8 @@ async function collectTlsIntelligence(hostname, options = {}) {
|
|
|
787
806
|
checkServerIdentity: () => undefined,
|
|
788
807
|
ALPNProtocols: ['h2', 'http/1.1'],
|
|
789
808
|
}, () => {
|
|
809
|
+
if (settled)
|
|
810
|
+
return;
|
|
790
811
|
try {
|
|
791
812
|
const peerCertificate = socket.getPeerCertificate(true);
|
|
792
813
|
let hostnameError = null;
|
|
@@ -816,6 +837,10 @@ async function collectTlsIntelligence(hostname, options = {}) {
|
|
|
816
837
|
fail(error);
|
|
817
838
|
}
|
|
818
839
|
});
|
|
840
|
+
if (settled) {
|
|
841
|
+
socket.destroy();
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
819
844
|
socket.once('error', fail);
|
|
820
845
|
deadline = setTimer(() => fail(new Error('TLS handshake timed out')), remainingMs);
|
|
821
846
|
if (settled && deadline !== undefined)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// objects so a later scan can explain why each domain was generated; callers
|
|
3
3
|
// that only need the historical string list can use the compatibility wrapper.
|
|
4
4
|
import { advancedConfusableVariantsForAscii, confusableCharactersForAscii, estimateAdvancedConfusableVariants, MAX_ADVANCED_CONFUSABLE_VARIANTS, wholeLabelConfusableVariantsForAscii, } from "./idn-confusables.mjs";
|
|
5
|
-
import { canonicalRegistrableDomain, normalizeDiscoverySuffix } from "
|
|
5
|
+
import { canonicalRegistrableDomain, normalizeDiscoverySuffix } from "../packages/analysis/registrable-domain.mjs";
|
|
6
6
|
import { MAX_DOMAIN_NAME_LENGTH, MAX_DOMAIN_LABEL_LENGTH } from "../packages/contracts/domain-name.mjs";
|
|
7
7
|
export { MAX_ADVANCED_CONFUSABLE_VARIANTS };
|
|
8
8
|
function buildKeyboardAdjacency(rows) {
|
|
@@ -775,12 +775,21 @@ export function generateTyposquatCandidateSet(rawInput, fallbackTlds, options =
|
|
|
775
775
|
const advanced = advancedConfusableVariantsForAscii(name);
|
|
776
776
|
const rejectedBefore = state.rejectedVariantCount;
|
|
777
777
|
let rejectedByEncoding = 0;
|
|
778
|
+
const encodedLabels = new Set();
|
|
779
|
+
let duplicatesAfterEncoding = 0;
|
|
778
780
|
for (const variant of advanced.variants) {
|
|
779
781
|
const ascii = toAsciiLabel(variant.unicodeLabel);
|
|
780
782
|
if (!ascii) {
|
|
781
783
|
rejectedByEncoding += 1;
|
|
782
784
|
continue;
|
|
783
785
|
}
|
|
786
|
+
// Different Unicode spellings can identify the same IDNA domain. They
|
|
787
|
+
// are duplicate candidates, not work omitted by a resource budget.
|
|
788
|
+
if (encodedLabels.has(ascii)) {
|
|
789
|
+
duplicatesAfterEncoding += 1;
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
encodedLabels.add(ascii);
|
|
784
793
|
addVariant(state, ascii, 'unicode_homoglyph_depth_2');
|
|
785
794
|
}
|
|
786
795
|
const generated = [...state.variants.values()]
|
|
@@ -788,7 +797,7 @@ export function generateTyposquatCandidateSet(rawInput, fallbackTlds, options =
|
|
|
788
797
|
.length;
|
|
789
798
|
const rejectedByValidation = state.rejectedVariantCount - rejectedBefore;
|
|
790
799
|
const omittedByBudget = advanced.omittedByBudget
|
|
791
|
-
+ Math.max(0, advanced.variants.length - rejectedByEncoding - rejectedByValidation - generated);
|
|
800
|
+
+ Math.max(0, advanced.variants.length - duplicatesAfterEncoding - rejectedByEncoding - rejectedByValidation - generated);
|
|
792
801
|
advancedConfusable = {
|
|
793
802
|
generated,
|
|
794
803
|
omittedByPolicy: advanced.omittedByPolicy + rejectedByEncoding + rejectedByValidation,
|