@adobe/spacecat-shared-data-access 3.69.0 → 3.70.1
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [@adobe/spacecat-shared-data-access-v3.70.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.70.0...@adobe/spacecat-shared-data-access-v3.70.1) (2026-05-22)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **data-access:** drop dangling belongs_to: Brand on BrandSemrushProject ([#1617](https://github.com/adobe/spacecat-shared/issues/1617)) ([b52d815](https://github.com/adobe/spacecat-shared/commit/b52d815a2e0e86af63464c71b899b5d1057236b0))
|
|
6
|
+
|
|
7
|
+
## [@adobe/spacecat-shared-data-access-v3.70.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.69.0...@adobe/spacecat-shared-data-access-v3.70.0) (2026-05-22)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **data-access:** extend DOMAIN_PATTERN to support subpath domains (LLMO-4187) ([#1593](https://github.com/adobe/spacecat-shared/issues/1593)) ([f6cd505](https://github.com/adobe/spacecat-shared/commit/f6cd505da6c61936538a39b26465c2bdc1c3863b))
|
|
12
|
+
|
|
1
13
|
## [@adobe/spacecat-shared-data-access-v3.69.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.68.0...@adobe/spacecat-shared-data-access-v3.69.0) (2026-05-22)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { hasText } from '@adobe/spacecat-shared-utils';
|
|
13
|
+
import { hasText, isValidUUID } from '@adobe/spacecat-shared-utils';
|
|
14
14
|
|
|
15
15
|
import SchemaBuilder from '../base/schema.builder.js';
|
|
16
16
|
import BrandSemrushProject from './brand-semrush-project.model.js';
|
|
@@ -26,9 +26,20 @@ import BrandSemrushProjectCollection from './brand-semrush-project.collection.js
|
|
|
26
26
|
const LANGUAGE_TAG_REGEX = /^[a-z]{2,3}(-[a-z]{2,4})?$/;
|
|
27
27
|
|
|
28
28
|
const schema = new SchemaBuilder(BrandSemrushProject, BrandSemrushProjectCollection)
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
29
|
+
// brandId is the FK to the brands table in mysticat-data-service. Declared
|
|
30
|
+
// explicitly (rather than via `.addReference('belongs_to', 'Brand')`)
|
|
31
|
+
// because this package does not ship a Brand entity — `belongs_to` would
|
|
32
|
+
// throw "Collection BrandCollection not found" at model instantiation
|
|
33
|
+
// (reference.js#toAccessorConfigs). The (brandId, updatedAt) index below
|
|
34
|
+
// produces the same `allByBrandId` accessor `belongs_to` would have.
|
|
35
|
+
// Swap back to `.addReference('belongs_to', 'Brand')` once Brand is
|
|
36
|
+
// registered.
|
|
37
|
+
.addAttribute('brandId', {
|
|
38
|
+
type: 'string',
|
|
39
|
+
required: true,
|
|
40
|
+
validate: (value) => isValidUUID(value),
|
|
41
|
+
})
|
|
42
|
+
.addIndex({ composite: ['brandId'] }, { composite: ['updatedAt'] })
|
|
32
43
|
.addAttribute('semrushProjectId', {
|
|
33
44
|
type: 'string',
|
|
34
45
|
required: true,
|
|
@@ -24,7 +24,90 @@ class PlgOnboarding extends BaseModel {
|
|
|
24
24
|
|
|
25
25
|
static IMS_ORG_ID_PATTERN = /^[a-z0-9]{24}@AdobeOrg$/i;
|
|
26
26
|
|
|
27
|
-
static
|
|
27
|
+
static MAX_HOSTNAME_LENGTH = 253; // RFC 1035 DNS name limit
|
|
28
|
+
|
|
29
|
+
// Practical cap, chosen for storage and sort-key index depth rather than for any
|
|
30
|
+
// specific browser/URL-bar limit (the domain field is a stored identifier, not a URL).
|
|
31
|
+
static MAX_DOMAIN_LENGTH = 2048;
|
|
32
|
+
|
|
33
|
+
// **WARNING for external consumers: do NOT use DOMAIN_PATTERN directly.**
|
|
34
|
+
// This regex is incomplete on its own — it has no length cap, no control-character
|
|
35
|
+
// rejection, no all-numeric-hostname check, no trailing-dot/consecutive-dot path
|
|
36
|
+
// rejection, and no typeof guard. Always call `PlgOnboarding.isValidDomain(value)`
|
|
37
|
+
// which composes this regex with the rest of the validator. The regex is exported
|
|
38
|
+
// only for legacy callers and may become module-private in a future major release.
|
|
39
|
+
//
|
|
40
|
+
// Matches lowercase hostnames (at least one dot required) and an optional subpath
|
|
41
|
+
// (e.g. nba.com, nba.com/kings, nba.com/us/kings).
|
|
42
|
+
// The final label (TLD) must be alphabetic (>= 2 chars) or punycode (xn--*). This
|
|
43
|
+
// structurally rejects every IP-literal form: dotted-quad (127.0.0.1), short-form
|
|
44
|
+
// (127.1), decimal (2130706433), hex (0x7f.0.0.1, 0xa9.254.169.254 → AWS IMDS),
|
|
45
|
+
// and octal (0177.0.0.1) — and also blocks foo.1-style typos. WHATWG URL would
|
|
46
|
+
// otherwise canonicalize hex/decimal IPs to their dotted-quad form, bypassing
|
|
47
|
+
// denylist-based SSRF gates downstream.
|
|
48
|
+
// Rejects: uppercase letters (use normalizeDomain() first), schemes (https://),
|
|
49
|
+
// ports (:8080), single-label hostnames (localhost, metadata), query strings,
|
|
50
|
+
// fragments, empty/trailing path segments, and any path segment starting with
|
|
51
|
+
// a dot (blocks ./, ../, .hidden, ..foo, etc.).
|
|
52
|
+
// Path-qualified domains (nba.com/kings) are distinct sort-key values from the bare
|
|
53
|
+
// hostname; callers must call normalizeDomain() before findByImsOrgIdAndDomain.
|
|
54
|
+
// Labels must not start or end with a hyphen (RFC 1035).
|
|
55
|
+
// Raw Unicode / IDN must be punycode-encoded before validation (xn-- form is accepted).
|
|
56
|
+
// Percent-encoded path characters (%20 etc.) are not accepted; decode before validation.
|
|
57
|
+
// Underscore is allowed in path segments but not in hostname labels.
|
|
58
|
+
static DOMAIN_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*\.(?:[a-z]{2,}|xn--[a-z0-9-]+)(\/(?!\.)[a-z0-9._~-]+)*$/;
|
|
59
|
+
|
|
60
|
+
// Returns the canonical form of a domain value: lowercased.
|
|
61
|
+
// Note: non-string inputs (null/undefined/number/object) are returned unchanged.
|
|
62
|
+
// Callers MUST also run `isValidDomain(value)` before using the result — calling
|
|
63
|
+
// `normalizeDomain` alone does not guarantee the value is a string or safe to
|
|
64
|
+
// pass to `findByImsOrgIdAndDomain` (which would otherwise treat a non-string
|
|
65
|
+
// sort key as something it isn't).
|
|
66
|
+
static normalizeDomain(value) {
|
|
67
|
+
return typeof value === 'string' ? value.toLowerCase() : value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Complete domain validator used by the schema and intended for external consumers.
|
|
71
|
+
// Layers a typeof guard, case-canonical check, control-character rejection,
|
|
72
|
+
// all-numeric-hostname rejection (defense-in-depth; DOMAIN_PATTERN's alphabetic-TLD
|
|
73
|
+
// requirement already rejects dotted-quad, short-form, decimal, hex, and octal IPs),
|
|
74
|
+
// trailing-dot path-segment rejection, DOMAIN_PATTERN test, and length caps.
|
|
75
|
+
// Note: DOMAIN_PATTERN alone is not sufficient — always prefer this method.
|
|
76
|
+
// Lowercase-only (host AND path) is intentional canonicalization, not a bug. The
|
|
77
|
+
// domain field is part of the dedup sort key on findByImsOrgIdAndDomain; allowing
|
|
78
|
+
// mixed-case paths would let `nba.com/Kings` and `nba.com/kings` create distinct
|
|
79
|
+
// onboarding rows for the same site. Callers should call normalizeDomain() first.
|
|
80
|
+
// This is a syntactic / data-integrity validator, not an SSRF gate. Callers that
|
|
81
|
+
// make outbound fetches must layer their own private-IP and DNS-resolution checks.
|
|
82
|
+
static isValidDomain(value) {
|
|
83
|
+
if (typeof value !== 'string' || value !== value.toLowerCase()) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
// Length caps run BEFORE the regex test so a multi-MB pathological input is
|
|
87
|
+
// rejected in O(1) rather than driving a multi-MB regex scan. The regex itself
|
|
88
|
+
// is linear (no overlapping quantifiers) but external consumers may not bound
|
|
89
|
+
// input size upstream.
|
|
90
|
+
if (value.length > PlgOnboarding.MAX_DOMAIN_LENGTH) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (/[^\x21-\x7e]/.test(value)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const [hostname, ...pathParts] = value.split('/');
|
|
97
|
+
if (hostname.length > PlgOnboarding.MAX_HOSTNAME_LENGTH) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (/^[\d.]+$/.test(hostname)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
// Reject path segments that are purely dots, end with a dot, or contain
|
|
104
|
+
// consecutive dots (foo., foo.., foo../bar, v1..0). DOMAIN_PATTERN's
|
|
105
|
+
// negative lookahead only blocks segments STARTING with a dot.
|
|
106
|
+
if (pathParts.some((seg) => /\.$/.test(seg) || seg.includes('..'))) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return PlgOnboarding.DOMAIN_PATTERN.test(value);
|
|
110
|
+
}
|
|
28
111
|
|
|
29
112
|
static STATUSES = {
|
|
30
113
|
PRE_ONBOARDING: 'PRE_ONBOARDING',
|
|
@@ -28,7 +28,7 @@ const schema = new SchemaBuilder(PlgOnboarding, PlgOnboardingCollection)
|
|
|
28
28
|
type: 'string',
|
|
29
29
|
required: true,
|
|
30
30
|
readOnly: true,
|
|
31
|
-
validate: (value) => PlgOnboarding.
|
|
31
|
+
validate: (value) => PlgOnboarding.isValidDomain(value),
|
|
32
32
|
})
|
|
33
33
|
.addAttribute('baseURL', {
|
|
34
34
|
type: 'string',
|