@slicedearth/whoisleuth-cli 1.43.0 → 1.47.4
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 +7 -3
- package/NOTICE +5 -4
- package/README.md +67 -10
- package/bin/whoisleuth.mjs +22 -9
- package/cli/arguments.mjs +256 -9
- package/cli/artifact-structure.mjs +1833 -0
- package/cli/artifact-verify.mjs +242 -49
- package/cli/bounded-json.mjs +1 -0
- package/cli/bulk-checkpoint.mjs +67 -31
- package/cli/bulk-command-runner.mjs +16 -6
- package/cli/bulk.mjs +18 -7
- package/cli/case-pack.mjs +819 -34
- package/cli/command-reference.mjs +48 -24
- package/cli/completion.mjs +50 -11
- package/cli/config-profile.mjs +15 -12
- package/cli/ct-event-intake.mjs +32 -2
- package/cli/discovery-observation-snapshot.mjs +8 -14
- package/cli/discovery-scan-command-runner.mjs +18 -6
- package/cli/discovery-snapshot.mjs +8 -15
- package/cli/discovery-workflow.mjs +2 -3
- package/cli/doctor.mjs +2 -2
- package/cli/domain-control-monitor.mjs +21 -15
- package/cli/domain-control-observations.mjs +25 -2
- package/cli/errors.mjs +7 -1
- package/cli/evidence-command-runner.mjs +17 -11
- package/cli/evidence-signing.mjs +73 -45
- package/cli/export-evidence.mjs +11 -4
- package/cli/formatters/html.mjs +3 -3
- package/cli/formatters/json.mjs +12 -4
- package/cli/formatters/terminal.mjs +4 -1
- package/cli/http.mjs +2 -1
- package/cli/input.mjs +57 -0
- package/cli/interchange-report.mjs +226 -0
- package/cli/investigation-manifest.mjs +16 -17
- package/cli/investigation-run.mjs +71 -23
- package/cli/local-mmdb-review.mjs +45 -14
- package/cli/lookup-command-runner.mjs +2 -2
- package/cli/lookup-diff.mjs +9 -0
- package/cli/lookup-reconcile.mjs +1 -1
- package/cli/mail-review.mjs +5 -5
- package/cli/manual.mjs +1 -1
- package/cli/network-command-runner.mjs +78 -0
- package/cli/offline-evidence-review.mjs +28 -8
- package/cli/page-compare.mjs +8 -5
- package/cli/registry-cohort.mjs +385 -61
- package/cli/registry-fixture-scaffold.mjs +4 -3
- package/cli/registry-support.mjs +2 -1
- package/cli/retained-artifact-diff.mjs +376 -0
- package/cli/risk-calibration.mjs +7 -5
- package/cli/runner.mjs +150 -177
- package/cli/saved-lookup.mjs +26 -3
- package/cli/sharing-review.mjs +12 -6
- package/docs/cli-reference.md +467 -74
- package/docs/cli.md +25 -5
- package/frontend/src/lib/analysis/acquisition-decision-packet.js +4 -4
- package/frontend/src/lib/analysis/acquisition-due-diligence.js +1 -1
- package/frontend/src/lib/analysis/artifact-integrity.js +42 -0
- package/frontend/src/lib/analysis/brand-profile-model.js +33 -7
- package/frontend/src/lib/analysis/bulk-domain-comparison.js +25 -5
- package/frontend/src/lib/analysis/bulk-mail-exposure.js +66 -15
- package/frontend/src/lib/analysis/bulk-retry-plan.js +1 -1
- package/frontend/src/lib/analysis/bulk-review-export.js +7 -6
- package/frontend/src/lib/analysis/bulk-review-model.js +3 -2
- package/frontend/src/lib/analysis/bulk-session-model.js +192 -26
- package/frontend/src/lib/analysis/bulk-sort.js +4 -0
- package/frontend/src/lib/analysis/case-brand-profile-references.js +85 -0
- package/frontend/src/lib/analysis/case-evidence-model.js +9 -1
- package/frontend/src/lib/analysis/case-investigation-branch-model.js +134 -0
- package/frontend/src/lib/analysis/case-migration-model.js +83 -31
- package/frontend/src/lib/analysis/case-record-contracts.js +4 -2
- package/frontend/src/lib/analysis/case-record-core.js +1 -1
- package/frontend/src/lib/analysis/case-record-model.js +2 -0
- package/frontend/src/lib/analysis/case-record-operations.js +43 -13
- package/frontend/src/lib/analysis/case-report.js +39 -6
- package/frontend/src/lib/analysis/case-response-model.js +66 -7
- package/frontend/src/lib/analysis/case-response-packet.js +15 -5
- package/frontend/src/lib/analysis/comparison-ledger-bulk.js +366 -0
- package/frontend/src/lib/analysis/comparison-ledger-contract.js +241 -0
- package/frontend/src/lib/analysis/comparison-ledger-json.js +79 -0
- package/frontend/src/lib/analysis/comparison-ledger-serialization.js +51 -0
- package/frontend/src/lib/analysis/domain-control-manifest-core.js +145 -0
- package/frontend/src/lib/analysis/domain-control-records.js +88 -0
- package/frontend/src/lib/analysis/external-findings-import.js +70 -6
- package/frontend/src/lib/analysis/investigation-capsule.js +47 -13
- package/frontend/src/lib/analysis/investigation-guide.js +52 -10
- package/frontend/src/lib/analysis/investigation-template-model.js +9 -6
- package/frontend/src/lib/analysis/lookup-asset-graph.js +8 -8
- package/frontend/src/lib/analysis/lookup-claim-passport.js +236 -0
- package/frontend/src/lib/analysis/lookup-claim-readiness.js +280 -0
- package/frontend/src/lib/analysis/lookup-decision-support.js +63 -8
- package/frontend/src/lib/analysis/lookup-presentation.js +70 -14
- package/frontend/src/lib/analysis/lookup-source-refresh.js +36 -63
- package/frontend/src/lib/analysis/opaque-reference-id.js +9 -0
- package/frontend/src/lib/analysis/utils.js +27 -4
- package/frontend/src/lib/analysis/visualization-matrix.js +71 -28
- package/frontend/src/lib/analysis/visualization-models.js +63 -94
- package/frontend/src/lib/analysis/visualization-time-series.js +53 -12
- package/frontend/src/lib/analysis/website-snapshot-model.js +6 -0
- package/frontend/src/lib/analysis/workspace-archive-crypto.js +1 -4
- package/frontend/src/lib/analysis/workspace-archive.js +105 -19
- package/lib/application-version.mjs +3 -0
- package/lib/availability.mjs +14 -12
- package/lib/bounded-file.mjs +82 -0
- package/lib/bounded-json-response.mjs +170 -0
- package/lib/bounded-json.mjs +249 -0
- package/lib/browser-library-profile.mjs +167 -38
- package/lib/certificate-extension-profile.mjs +2 -1
- package/lib/cryptographic-assurance.mjs +155 -0
- package/lib/ct-search.mjs +87 -47
- package/lib/dns-intelligence.mjs +3 -2
- package/lib/dns-resolver-selection.mjs +6 -4
- package/lib/dnssec-chain-validation.mjs +1053 -0
- package/lib/dnssec-evidence-validation.mjs +10 -3
- package/lib/domain-change-packet.mjs +7 -5
- package/lib/domain-control-manifest.mjs +17 -129
- package/lib/domain-posture.mjs +7 -4
- package/lib/evidence-export.mjs +751 -78
- package/lib/evidence-report-markdown.mjs +3 -0
- package/lib/evidence-report.mjs +5 -0
- package/lib/favicon.mjs +8 -5
- package/lib/http-evidence-bounds.mjs +3 -0
- package/lib/http-intelligence.mjs +2 -5
- package/lib/interchange-fidelity-registry.mjs +122 -0
- package/lib/local-geoip-evidence.mjs +2 -1
- package/lib/lookup-cache.mjs +5 -1
- package/lib/lookup-network-evidence-bounds.mjs +9 -0
- package/lib/lookup-response-contract.mjs +691 -3
- package/lib/lookup.mjs +2 -2
- package/lib/observation.mjs +7 -6
- package/lib/outbound-identity.mjs +2 -3
- package/lib/perceptual-hash.mjs +12 -4
- package/lib/portable-generator.mjs +21 -0
- package/lib/provider-policy-admission.mjs +9 -4
- package/lib/rdap-normalization.mjs +18 -5
- package/lib/rdap-registrar.mjs +5 -1
- package/lib/rdap-transport.mjs +6 -6
- package/lib/registry-capabilities.mjs +3 -0
- package/lib/registry-capability-catalogue.mjs +12 -2
- package/lib/registry-comparison.mjs +28 -11
- package/lib/risk-calibration-summary.mjs +338 -0
- package/lib/rpki-evidence.mjs +9 -8
- package/lib/safe-fetch.mjs +96 -42
- package/lib/security-txt.mjs +26 -3
- package/lib/service-binding-dns.mjs +6 -1
- package/lib/smtp-transport-review.mjs +784 -0
- package/lib/sslbl-certificates.generated.mjs +136 -134
- package/lib/threat-intelligence-runtime.mjs +4 -13
- package/lib/threat-intelligence-types.mjs +18 -1
- package/lib/tls-intelligence.mjs +11 -5
- package/lib/tlsa-evidence.mjs +37 -7
- package/lib/website-technology.mjs +2 -6
- package/package.json +1 -1
- package/third-party-notices.txt +95 -5817
|
@@ -4,23 +4,15 @@
|
|
|
4
4
|
// must declare exact input exposure, reviewed terms, and bounded request and
|
|
5
5
|
// output budgets before producing separately attributed normalized evidence.
|
|
6
6
|
import { createObservation } from "./observation.mjs";
|
|
7
|
+
import { THREAT_INTELLIGENCE_CATEGORIES, THREAT_INTELLIGENCE_RESULT_STATES, } from "./threat-intelligence-types.mjs";
|
|
7
8
|
import { assertCuratedConnectorDefinition, assertThreatIntelligenceProvider, } from "./threat-intelligence-definition-registry.mjs";
|
|
8
|
-
import { boundedHttpsUrl,
|
|
9
|
+
import { boundedHttpsUrl, boundedString, isRecord, isoTimestamp, strictBoundedString, } from "./bounded-contract-normalizers.mjs";
|
|
9
10
|
import { CURATED_CONNECTOR_ENTITY_TYPES, MAX_THREAT_INTELLIGENCE_URL_LENGTH, normalizeCuratedConnectorTarget, normalizeThreatIntelligenceTarget, } from "./threat-intelligence-targets.mjs";
|
|
10
11
|
const THREAT_INTELLIGENCE_CONTRACT_VERSION = 1;
|
|
11
12
|
const THREAT_INTELLIGENCE_SCHEMA = 'whoisleuth.threat-intelligence-result';
|
|
12
13
|
const CURATED_CONNECTOR_CONTRACT_VERSION = 1;
|
|
13
14
|
const CURATED_CONNECTOR_RESULT_SCHEMA = 'whoisleuth.curated-connector-result';
|
|
14
|
-
const RESULT_STATES = new Set(
|
|
15
|
-
'success',
|
|
16
|
-
'partial',
|
|
17
|
-
'not_found',
|
|
18
|
-
'unsupported',
|
|
19
|
-
'skipped',
|
|
20
|
-
'rate_limited',
|
|
21
|
-
'unavailable',
|
|
22
|
-
'error',
|
|
23
|
-
]);
|
|
15
|
+
const RESULT_STATES = new Set(THREAT_INTELLIGENCE_RESULT_STATES);
|
|
24
16
|
const TERMINAL_STATES_WITHOUT_FINDINGS = new Set([
|
|
25
17
|
'not_found',
|
|
26
18
|
'unsupported',
|
|
@@ -29,7 +21,7 @@ const TERMINAL_STATES_WITHOUT_FINDINGS = new Set([
|
|
|
29
21
|
'unavailable',
|
|
30
22
|
'error',
|
|
31
23
|
]);
|
|
32
|
-
const CATEGORIES = new Set(
|
|
24
|
+
const CATEGORIES = new Set(THREAT_INTELLIGENCE_CATEGORIES);
|
|
33
25
|
const SEVERITIES = new Set(['critical', 'high', 'medium', 'low', 'unknown']);
|
|
34
26
|
const CONFIDENCES = new Set(['high', 'medium', 'low', 'unknown']);
|
|
35
27
|
const CONNECTOR_RELATIONSHIP_TYPES = new Set([
|
|
@@ -78,7 +70,6 @@ const CONNECTOR_RELATIONSHIP_ENDPOINTS = Object.freeze({
|
|
|
78
70
|
});
|
|
79
71
|
// createObservation() retains at most 40 source characters, so provider IDs use
|
|
80
72
|
// the same ceiling and can never be truncated at the provenance boundary.
|
|
81
|
-
const MAX_PROVIDER_ID_LENGTH = 40;
|
|
82
73
|
const MAX_PROVIDER_LABEL_LENGTH = 100;
|
|
83
74
|
const MAX_DETAIL_LENGTH = 500;
|
|
84
75
|
const MAX_FINDING_ID_LENGTH = 160;
|
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
// Provider-neutral types shared by threat-intelligence and curated connector
|
|
2
2
|
// manifests, fixture harnesses, normalizers, and result builders.
|
|
3
|
-
export
|
|
3
|
+
export const THREAT_INTELLIGENCE_CATEGORIES = Object.freeze([
|
|
4
|
+
'phishing',
|
|
5
|
+
'malware',
|
|
6
|
+
'spam',
|
|
7
|
+
'suspicious',
|
|
8
|
+
'abuse',
|
|
9
|
+
'unknown',
|
|
10
|
+
]);
|
|
11
|
+
export const THREAT_INTELLIGENCE_RESULT_STATES = Object.freeze([
|
|
12
|
+
'success',
|
|
13
|
+
'partial',
|
|
14
|
+
'not_found',
|
|
15
|
+
'unsupported',
|
|
16
|
+
'skipped',
|
|
17
|
+
'rate_limited',
|
|
18
|
+
'unavailable',
|
|
19
|
+
'error',
|
|
20
|
+
]);
|
package/lib/tls-intelligence.mjs
CHANGED
|
@@ -11,18 +11,19 @@ import * as net from 'node:net';
|
|
|
11
11
|
import * as tls from 'node:tls';
|
|
12
12
|
import { domainToASCII } from 'node:url';
|
|
13
13
|
import { createObservation } from "./observation.mjs";
|
|
14
|
+
import { MAX_LOOKUP_TLS_ALT_NAMES, MAX_LOOKUP_TLS_CHAIN_CERTIFICATES, MAX_LOOKUP_TLS_FINDINGS, MAX_LOOKUP_TLS_NAME_VALUES, } from "./lookup-network-evidence-bounds.mjs";
|
|
14
15
|
import { isPrivateAddress, resolvePublicAddresses } from "./safe-fetch.mjs";
|
|
15
16
|
import { parseCertificateExtensionProfile, } from "./certificate-extension-profile.mjs";
|
|
16
17
|
const TLS_PROFILE_VERSION = 3;
|
|
17
18
|
const TLS_PORT = 443;
|
|
18
19
|
const TLS_TIMEOUT_MS = 5000;
|
|
19
20
|
const MAX_RESOLVED_ADDRESSES = 64;
|
|
20
|
-
const MAX_CHAIN_CERTIFICATES =
|
|
21
|
-
const MAX_ALT_NAMES =
|
|
21
|
+
const MAX_CHAIN_CERTIFICATES = MAX_LOOKUP_TLS_CHAIN_CERTIFICATES;
|
|
22
|
+
const MAX_ALT_NAMES = MAX_LOOKUP_TLS_ALT_NAMES;
|
|
22
23
|
const MAX_SAN_ENTRIES = 100;
|
|
23
24
|
const MAX_EXTENDED_KEY_USAGES = 16;
|
|
24
25
|
const MAX_AIA_LOCATIONS = 32;
|
|
25
|
-
const MAX_NAME_VALUES =
|
|
26
|
+
const MAX_NAME_VALUES = MAX_LOOKUP_TLS_NAME_VALUES;
|
|
26
27
|
const MAX_TEXT_LENGTH = 256;
|
|
27
28
|
const MAX_ERROR_LENGTH = 240;
|
|
28
29
|
const MAX_SERIAL_LENGTH = 128;
|
|
@@ -577,7 +578,7 @@ function tlsFindings(profile) {
|
|
|
577
578
|
findings.push({ id: 'hostname_mismatch', tone: 'warning', label: 'Hostname mismatch', detail: 'The observed leaf certificate did not match the SNI hostname.' });
|
|
578
579
|
if (profile.certificate?.subjectAltNames?.dnsNames.some((name) => name.startsWith('*.')))
|
|
579
580
|
findings.push({ id: 'wildcard_certificate', tone: 'neutral', label: 'Wildcard certificate', detail: 'The leaf certificate includes at least one wildcard DNS name. Wildcard use is common and is not inherently suspicious.' });
|
|
580
|
-
return findings;
|
|
581
|
+
return findings.slice(0, MAX_LOOKUP_TLS_FINDINGS);
|
|
581
582
|
}
|
|
582
583
|
function buildTlsObservation(handshake = {}, options = {}) {
|
|
583
584
|
const state = tracker();
|
|
@@ -590,7 +591,9 @@ function buildTlsObservation(handshake = {}, options = {}) {
|
|
|
590
591
|
const authorized = typeof handshake.authorized === 'boolean' ? handshake.authorized : null;
|
|
591
592
|
const hostnameMatches = typeof handshake.hostnameMatches === 'boolean' ? handshake.hostnameMatches : null;
|
|
592
593
|
const authorizationError = authorized === false
|
|
593
|
-
? boundedString(handshake.authorizationError
|
|
594
|
+
? boundedString(handshake.authorizationError instanceof Error
|
|
595
|
+
? handshake.authorizationError.message
|
|
596
|
+
: handshake.authorizationError, MAX_ERROR_LENGTH, state)
|
|
594
597
|
: null;
|
|
595
598
|
const hostnameError = hostnameMatches === false
|
|
596
599
|
? boundedString(handshake.hostnameError, MAX_ERROR_LENGTH, state)
|
|
@@ -780,6 +783,9 @@ async function collectTlsIntelligence(hostname, options = {}) {
|
|
|
780
783
|
port: TLS_PORT,
|
|
781
784
|
servername: normalizedHostname,
|
|
782
785
|
rejectUnauthorized: false,
|
|
786
|
+
// Keep runtime CA-path authorization independent from the separately
|
|
787
|
+
// recorded endpoint-identity comparison below.
|
|
788
|
+
checkServerIdentity: () => undefined,
|
|
783
789
|
ALPNProtocols: ['h2', 'http/1.1'],
|
|
784
790
|
}, () => {
|
|
785
791
|
try {
|
package/lib/tlsa-evidence.mjs
CHANGED
|
@@ -128,7 +128,15 @@ function analyzeTlsaEvidence(input) {
|
|
|
128
128
|
const normalized = raw.slice(0, MAX_TLSA_RECORDS).map(normalizeTlsaRecord);
|
|
129
129
|
const rejectedCount = normalized.filter((item) => item === null).length + Math.max(0, raw.length - MAX_TLSA_RECORDS);
|
|
130
130
|
const records = normalized.filter((item) => item !== null);
|
|
131
|
+
const smtpRelayService = service.port === 25 && service.transport === 'tcp';
|
|
131
132
|
const results = records.map((item) => {
|
|
133
|
+
if (smtpRelayService && (item.usage === 0 || item.usage === 1)) {
|
|
134
|
+
return Object.freeze({
|
|
135
|
+
...item,
|
|
136
|
+
state: 'unsupported',
|
|
137
|
+
reason: 'PKIX-TA and PKIX-EE certificate usages are not treated as usable TLSA records for SMTP relay on port 25.',
|
|
138
|
+
});
|
|
139
|
+
}
|
|
132
140
|
const materials = item.usage === 0 || item.usage === 2
|
|
133
141
|
? authorityMaterials
|
|
134
142
|
: leafMaterial ? [leafMaterial] : [];
|
|
@@ -147,6 +155,7 @@ function analyzeTlsaEvidence(input) {
|
|
|
147
155
|
const hasMatch = results.some((item) => item.state === 'matched');
|
|
148
156
|
const hasDifferent = results.some((item) => item.state === 'different');
|
|
149
157
|
const hasUnavailable = results.some((item) => item.state === 'unavailable');
|
|
158
|
+
const hasUnsupported = results.some((item) => item.state === 'unsupported');
|
|
150
159
|
const limitations = [
|
|
151
160
|
'This offline comparison does not connect to the target, retrieve DNS, negotiate SMTP STARTTLS, or validate a DNSSEC chain.',
|
|
152
161
|
'A TLSA match is usable as DANE evidence only when the DNSSEC state was independently validated for the same observation.',
|
|
@@ -155,24 +164,45 @@ function analyzeTlsaEvidence(input) {
|
|
|
155
164
|
if (dnssecState !== 'validated' && hasMatch) {
|
|
156
165
|
limitations.push('Certificate material matched, but DNSSEC was not validated, so the result remains partial.');
|
|
157
166
|
}
|
|
158
|
-
|
|
159
|
-
|
|
167
|
+
if (smtpRelayService && hasUnsupported) {
|
|
168
|
+
limitations.push('RFC 7672 leaves SMTP relay treatment of PKIX-TA usage 0 and PKIX-EE usage 1 undefined; this review treats those records as unusable and they cannot complete SMTP DANE assurance.');
|
|
169
|
+
}
|
|
170
|
+
const matchedPkixTa = results.some((item) => item.state === 'matched' && item.usage === 0);
|
|
171
|
+
const matchedPkixEe = results.some((item) => item.state === 'matched' && item.usage === 1);
|
|
172
|
+
const matchedPkixDependent = matchedPkixTa || matchedPkixEe;
|
|
173
|
+
const matchedDaneEe = results.some((item) => item.state === 'matched' && item.usage === 3);
|
|
174
|
+
const hasPkixTaAssociation = results.some((item) => item.usage === 0);
|
|
175
|
+
const hasDaneTaAssociation = results.some((item) => item.usage === 2);
|
|
160
176
|
if (matchedPkixDependent && pkixValidationState !== 'validated') {
|
|
161
177
|
limitations.push('A PKIX-dependent association matched, but a validated PKIX path was not supplied, so the result is not complete.');
|
|
162
178
|
}
|
|
163
179
|
if (authorityMaterialRejectedCount > 0 || authorityMaterialTruncated) {
|
|
164
180
|
limitations.push('Authority certificate material was malformed or exceeded the review bound, so an unmatched trust-anchor association remains partial.');
|
|
165
181
|
}
|
|
182
|
+
if (hasPkixTaAssociation) {
|
|
183
|
+
limitations.push('PKIX-TA usage 0 association bytes were compared, but the supplied authority material was not proven to be part of the independently validated leaf path, so the usage-0 association cannot complete PKIX-TA assurance.');
|
|
184
|
+
}
|
|
185
|
+
if (hasDaneTaAssociation) {
|
|
186
|
+
limitations.push('DANE-TA usage 2 association bytes were compared, but no certificate path from the observed leaf to the supplied trust-anchor material was constructed or validated, so the usage-2 association cannot complete DANE-TA assurance.');
|
|
187
|
+
}
|
|
188
|
+
if (hasUnavailable || rejectedCount > 0 || truncated) {
|
|
189
|
+
limitations.push('Some TLSA associations or required certificate material were unavailable, malformed, or beyond the review bound, so the comparison remains incomplete.');
|
|
190
|
+
}
|
|
191
|
+
const comparisonMaterialComplete = !hasUnavailable
|
|
192
|
+
&& rejectedCount === 0
|
|
193
|
+
&& !truncated
|
|
194
|
+
&& authorityMaterialRejectedCount === 0
|
|
195
|
+
&& !authorityMaterialTruncated;
|
|
166
196
|
const trustedMatch = dnssecState === 'validated'
|
|
167
|
-
&& (
|
|
197
|
+
&& (matchedDaneEe || matchedPkixEe && pkixValidationState === 'validated');
|
|
168
198
|
const state = results.length === 0
|
|
169
199
|
? 'unavailable'
|
|
170
|
-
:
|
|
171
|
-
&&
|
|
200
|
+
: dnssecState === 'validated' && hasDifferent && !hasMatch && comparisonMaterialComplete
|
|
201
|
+
&& !hasPkixTaAssociation && !hasDaneTaAssociation
|
|
172
202
|
? 'different'
|
|
173
|
-
: hasMatch && (dnssecState === 'bogus' || matchedPkixDependent && pkixValidationState === 'failed' && !
|
|
203
|
+
: hasMatch && (dnssecState === 'bogus' || matchedPkixDependent && pkixValidationState === 'failed' && !matchedDaneEe)
|
|
174
204
|
? 'untrusted'
|
|
175
|
-
: trustedMatch &&
|
|
205
|
+
: trustedMatch && comparisonMaterialComplete
|
|
176
206
|
? 'matched'
|
|
177
207
|
: 'partial';
|
|
178
208
|
return Object.freeze({
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { analyzeBrowserLibraries } from "./browser-library-profile.mjs";
|
|
8
8
|
import { createObservation } from "./observation.mjs";
|
|
9
9
|
import { MAX_STATIC_HTML_CHARS, MAX_TAG_LENGTH, MAX_TECHNOLOGY_TAGS, analyzeStaticHtml, } from "./static-html-analysis.mjs";
|
|
10
|
-
const TECHNOLOGY_PROFILE_VERSION =
|
|
10
|
+
const TECHNOLOGY_PROFILE_VERSION = 10;
|
|
11
11
|
const MAX_TECHNOLOGY_HTML_CHARS = MAX_STATIC_HTML_CHARS;
|
|
12
12
|
const MAX_TECHNOLOGY_TAG_LENGTH = MAX_TAG_LENGTH;
|
|
13
13
|
const MAX_TECHNOLOGY_FINDINGS = 24;
|
|
@@ -151,7 +151,6 @@ const TECHNOLOGY_SIGNATURES = [
|
|
|
151
151
|
{
|
|
152
152
|
id: 'craft-cms', name: 'Craft CMS', category: 'content management',
|
|
153
153
|
evidence: [
|
|
154
|
-
generatorEvidence(/^craft cms(?:\s|$)/i, 'Generator metadata identifies Craft CMS.'),
|
|
155
154
|
responseHeaderEvidence('x-powered-by', /(?:^|,\s*)craft cms(?:\s|$|\/)/i, 'The passive X-Powered-By response header identifies Craft CMS.'),
|
|
156
155
|
],
|
|
157
156
|
},
|
|
@@ -204,14 +203,12 @@ const TECHNOLOGY_SIGNATURES = [
|
|
|
204
203
|
{
|
|
205
204
|
id: 'opencart', name: 'OpenCart', category: 'commerce',
|
|
206
205
|
evidence: [
|
|
207
|
-
generatorEvidence(/^opencart(?:\s|$)/i, 'Generator metadata identifies OpenCart.'),
|
|
208
206
|
htmlEvidence(['index.php?route=common/home', 'image/catalog/opencart-logo.png'], 'Static markup contains OpenCart routing or default asset conventions.'),
|
|
209
207
|
],
|
|
210
208
|
},
|
|
211
209
|
{
|
|
212
210
|
id: 'prestashop', name: 'PrestaShop', category: 'commerce',
|
|
213
211
|
evidence: [
|
|
214
|
-
generatorEvidence(/^prestashop(?:\s|$)/i, 'Generator metadata identifies PrestaShop.'),
|
|
215
212
|
htmlEvidence(['/modules/ps_'], 'Static resource paths use PrestaShop module conventions.'),
|
|
216
213
|
],
|
|
217
214
|
},
|
|
@@ -253,7 +250,7 @@ const TECHNOLOGY_SIGNATURES = [
|
|
|
253
250
|
id: 'weebly', name: 'Weebly', category: 'site builder',
|
|
254
251
|
requiresNonResourceEvidence: true,
|
|
255
252
|
evidence: [
|
|
256
|
-
|
|
253
|
+
htmlEvidence(['id="wsite-base-style"', "id='wsite-base-style'", 'title="wsite-theme-css"', "title='wsite-theme-css'"], 'Static markup contains Weebly-specific theme attributes.'),
|
|
257
254
|
resourceEvidence(['editmysite.com'], 'A retained resource origin uses Weebly delivery infrastructure.'),
|
|
258
255
|
],
|
|
259
256
|
},
|
|
@@ -321,7 +318,6 @@ const TECHNOLOGY_SIGNATURES = [
|
|
|
321
318
|
{
|
|
322
319
|
id: 'cloudfront', name: 'Amazon CloudFront', category: 'delivery platform',
|
|
323
320
|
evidence: [
|
|
324
|
-
serverEvidence(/^cloudfront(?:\s|$|\/)/i, 'The selected response server header identifies Amazon CloudFront.'),
|
|
325
321
|
resourceEvidence(['cloudfront.net'], 'A retained resource origin uses Amazon CloudFront delivery infrastructure.'),
|
|
326
322
|
],
|
|
327
323
|
},
|