@linkforty/core 1.44.1 → 1.44.3
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/README.md +20 -588
- package/llms.txt +1 -1
- package/package.json +2 -1
- package/dist/index.config.test.d.ts +0 -2
- package/dist/index.config.test.d.ts.map +0 -1
- package/dist/index.config.test.js +0 -87
- package/dist/index.config.test.js.map +0 -1
- package/dist/lib/bot-detection.test.d.ts +0 -2
- package/dist/lib/bot-detection.test.d.ts.map +0 -1
- package/dist/lib/bot-detection.test.js +0 -65
- package/dist/lib/bot-detection.test.js.map +0 -1
- package/dist/lib/client-ip.test.d.ts +0 -7
- package/dist/lib/client-ip.test.d.ts.map +0 -1
- package/dist/lib/client-ip.test.js +0 -114
- package/dist/lib/client-ip.test.js.map +0 -1
- package/dist/lib/config.test.d.ts +0 -2
- package/dist/lib/config.test.d.ts.map +0 -1
- package/dist/lib/config.test.js +0 -126
- package/dist/lib/config.test.js.map +0 -1
- package/dist/lib/database.test.d.ts +0 -2
- package/dist/lib/database.test.d.ts.map +0 -1
- package/dist/lib/database.test.js +0 -180
- package/dist/lib/database.test.js.map +0 -1
- package/dist/lib/deep-link-payload.test.d.ts +0 -2
- package/dist/lib/deep-link-payload.test.d.ts.map +0 -1
- package/dist/lib/deep-link-payload.test.js +0 -202
- package/dist/lib/deep-link-payload.test.js.map +0 -1
- package/dist/lib/event-emitter.test.d.ts +0 -2
- package/dist/lib/event-emitter.test.d.ts.map +0 -1
- package/dist/lib/event-emitter.test.js +0 -162
- package/dist/lib/event-emitter.test.js.map +0 -1
- package/dist/lib/fingerprint.test.d.ts +0 -2
- package/dist/lib/fingerprint.test.d.ts.map +0 -1
- package/dist/lib/fingerprint.test.js +0 -750
- package/dist/lib/fingerprint.test.js.map +0 -1
- package/dist/lib/launchpad.test.d.ts +0 -2
- package/dist/lib/launchpad.test.d.ts.map +0 -1
- package/dist/lib/launchpad.test.js +0 -368
- package/dist/lib/launchpad.test.js.map +0 -1
- package/dist/lib/link-lookup.test.d.ts +0 -2
- package/dist/lib/link-lookup.test.d.ts.map +0 -1
- package/dist/lib/link-lookup.test.js +0 -61
- package/dist/lib/link-lookup.test.js.map +0 -1
- package/dist/lib/link-resolution-cache.test.d.ts +0 -2
- package/dist/lib/link-resolution-cache.test.d.ts.map +0 -1
- package/dist/lib/link-resolution-cache.test.js +0 -45
- package/dist/lib/link-resolution-cache.test.js.map +0 -1
- package/dist/lib/link-safety.test.d.ts +0 -2
- package/dist/lib/link-safety.test.d.ts.map +0 -1
- package/dist/lib/link-safety.test.js +0 -211
- package/dist/lib/link-safety.test.js.map +0 -1
- package/dist/lib/not-found-page.test.d.ts +0 -2
- package/dist/lib/not-found-page.test.d.ts.map +0 -1
- package/dist/lib/not-found-page.test.js +0 -151
- package/dist/lib/not-found-page.test.js.map +0 -1
- package/dist/lib/portable/cli.test.d.ts +0 -2
- package/dist/lib/portable/cli.test.d.ts.map +0 -1
- package/dist/lib/portable/cli.test.js +0 -98
- package/dist/lib/portable/cli.test.js.map +0 -1
- package/dist/lib/portable/portable.integration.test.d.ts +0 -2
- package/dist/lib/portable/portable.integration.test.d.ts.map +0 -1
- package/dist/lib/portable/portable.integration.test.js +0 -139
- package/dist/lib/portable/portable.integration.test.js.map +0 -1
- package/dist/lib/portable/registry.schema.test.d.ts +0 -2
- package/dist/lib/portable/registry.schema.test.d.ts.map +0 -1
- package/dist/lib/portable/registry.schema.test.js +0 -59
- package/dist/lib/portable/registry.schema.test.js.map +0 -1
- package/dist/lib/scanner-detection.test.d.ts +0 -2
- package/dist/lib/scanner-detection.test.d.ts.map +0 -1
- package/dist/lib/scanner-detection.test.js +0 -124
- package/dist/lib/scanner-detection.test.js.map +0 -1
- package/dist/lib/schema-guard.test.d.ts +0 -2
- package/dist/lib/schema-guard.test.d.ts.map +0 -1
- package/dist/lib/schema-guard.test.js +0 -83
- package/dist/lib/schema-guard.test.js.map +0 -1
- package/dist/lib/utils.test.d.ts +0 -2
- package/dist/lib/utils.test.d.ts.map +0 -1
- package/dist/lib/utils.test.js +0 -261
- package/dist/lib/utils.test.js.map +0 -1
- package/dist/lib/webhook.test.d.ts +0 -2
- package/dist/lib/webhook.test.d.ts.map +0 -1
- package/dist/lib/webhook.test.js +0 -340
- package/dist/lib/webhook.test.js.map +0 -1
- package/dist/routes/health.test.d.ts +0 -2
- package/dist/routes/health.test.d.ts.map +0 -1
- package/dist/routes/health.test.js +0 -82
- package/dist/routes/health.test.js.map +0 -1
- package/dist/routes/preview.test.d.ts +0 -2
- package/dist/routes/preview.test.d.ts.map +0 -1
- package/dist/routes/preview.test.js +0 -111
- package/dist/routes/preview.test.js.map +0 -1
- package/dist/routes/qr.test.d.ts +0 -2
- package/dist/routes/qr.test.d.ts.map +0 -1
- package/dist/routes/qr.test.js +0 -67
- package/dist/routes/qr.test.js.map +0 -1
- package/dist/routes/redirect.click-event.test.d.ts +0 -2
- package/dist/routes/redirect.click-event.test.d.ts.map +0 -1
- package/dist/routes/redirect.click-event.test.js +0 -85
- package/dist/routes/redirect.click-event.test.js.map +0 -1
- package/dist/routes/redirect.host-scope.integration.test.d.ts +0 -2
- package/dist/routes/redirect.host-scope.integration.test.d.ts.map +0 -1
- package/dist/routes/redirect.host-scope.integration.test.js +0 -105
- package/dist/routes/redirect.host-scope.integration.test.js.map +0 -1
- package/dist/routes/redirect.interstitial.test.d.ts +0 -2
- package/dist/routes/redirect.interstitial.test.d.ts.map +0 -1
- package/dist/routes/redirect.interstitial.test.js +0 -128
- package/dist/routes/redirect.interstitial.test.js.map +0 -1
- package/dist/routes/redirect.launchpad.test.d.ts +0 -2
- package/dist/routes/redirect.launchpad.test.d.ts.map +0 -1
- package/dist/routes/redirect.launchpad.test.js +0 -515
- package/dist/routes/redirect.launchpad.test.js.map +0 -1
- package/dist/routes/redirect.no-web-destination.test.d.ts +0 -2
- package/dist/routes/redirect.no-web-destination.test.d.ts.map +0 -1
- package/dist/routes/redirect.no-web-destination.test.js +0 -176
- package/dist/routes/redirect.no-web-destination.test.js.map +0 -1
- package/dist/routes/redirect.not-found.test.d.ts +0 -2
- package/dist/routes/redirect.not-found.test.d.ts.map +0 -1
- package/dist/routes/redirect.not-found.test.js +0 -280
- package/dist/routes/redirect.not-found.test.js.map +0 -1
- package/dist/routes/redirect.safety.test.d.ts +0 -2
- package/dist/routes/redirect.safety.test.d.ts.map +0 -1
- package/dist/routes/redirect.safety.test.js +0 -284
- package/dist/routes/redirect.safety.test.js.map +0 -1
- package/dist/routes/redirect.store-url.test.d.ts +0 -2
- package/dist/routes/redirect.store-url.test.d.ts.map +0 -1
- package/dist/routes/redirect.store-url.test.js +0 -239
- package/dist/routes/redirect.store-url.test.js.map +0 -1
- package/dist/routes/redirect.test.d.ts +0 -2
- package/dist/routes/redirect.test.d.ts.map +0 -1
- package/dist/routes/redirect.test.js +0 -158
- package/dist/routes/redirect.test.js.map +0 -1
- package/dist/routes/redirect.web-link.test.d.ts +0 -2
- package/dist/routes/redirect.web-link.test.d.ts.map +0 -1
- package/dist/routes/redirect.web-link.test.js +0 -138
- package/dist/routes/redirect.web-link.test.js.map +0 -1
- package/dist/routes/sdk-cache-bypass.test.d.ts +0 -2
- package/dist/routes/sdk-cache-bypass.test.d.ts.map +0 -1
- package/dist/routes/sdk-cache-bypass.test.js +0 -175
- package/dist/routes/sdk-cache-bypass.test.js.map +0 -1
- package/dist/routes/sdk.device-values.integration.test.d.ts +0 -2
- package/dist/routes/sdk.device-values.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.device-values.integration.test.js +0 -132
- package/dist/routes/sdk.device-values.integration.test.js.map +0 -1
- package/dist/routes/sdk.event.test.d.ts +0 -2
- package/dist/routes/sdk.event.test.d.ts.map +0 -1
- package/dist/routes/sdk.event.test.js +0 -284
- package/dist/routes/sdk.event.test.js.map +0 -1
- package/dist/routes/sdk.install-certainty.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-certainty.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-certainty.integration.test.js +0 -104
- package/dist/routes/sdk.install-certainty.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-live-links.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-live-links.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-live-links.integration.test.js +0 -92
- package/dist/routes/sdk.install-live-links.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-referrer.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-referrer.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-referrer.integration.test.js +0 -187
- package/dist/routes/sdk.install-referrer.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-window-once.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-window-once.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-window-once.integration.test.js +0 -147
- package/dist/routes/sdk.install-window-once.integration.test.js.map +0 -1
- package/dist/routes/sdk.install.test.d.ts +0 -2
- package/dist/routes/sdk.install.test.d.ts.map +0 -1
- package/dist/routes/sdk.install.test.js +0 -99
- package/dist/routes/sdk.install.test.js.map +0 -1
- package/dist/routes/sdk.resolve-click.test.d.ts +0 -2
- package/dist/routes/sdk.resolve-click.test.d.ts.map +0 -1
- package/dist/routes/sdk.resolve-click.test.js +0 -79
- package/dist/routes/sdk.resolve-click.test.js.map +0 -1
- package/dist/routes/sdk.resolve-scope.test.d.ts +0 -2
- package/dist/routes/sdk.resolve-scope.test.d.ts.map +0 -1
- package/dist/routes/sdk.resolve-scope.test.js +0 -105
- package/dist/routes/sdk.resolve-scope.test.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-resolution-cache.test.js","sourceRoot":"","sources":["../../src/lib/link-resolution-cache.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-safety.test.d.ts","sourceRoot":"","sources":["../../src/lib/link-safety.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { evaluateLinkSafety, generateWarningLinkHTML, escapeHtml, safeHref, evaluateLinkSafetyDecision, blockCauseIsAbuse, generateBlockedLinkHTML, } from './link-safety.js';
|
|
3
|
-
describe('evaluateLinkSafety', () => {
|
|
4
|
-
it('allows a plain healthy link', () => {
|
|
5
|
-
expect(evaluateLinkSafety({ isActive: true })).toBe('allow');
|
|
6
|
-
expect(evaluateLinkSafety({})).toBe('allow');
|
|
7
|
-
});
|
|
8
|
-
it('treats an absent isActive as active, so existing rows are unaffected', () => {
|
|
9
|
-
expect(evaluateLinkSafety({ isActive: undefined })).toBe('allow');
|
|
10
|
-
expect(evaluateLinkSafety({ isActive: null })).toBe('allow');
|
|
11
|
-
});
|
|
12
|
-
it('blocks an inactive link', () => {
|
|
13
|
-
expect(evaluateLinkSafety({ isActive: false })).toBe('block');
|
|
14
|
-
});
|
|
15
|
-
it('warns when warn_at is set', () => {
|
|
16
|
-
expect(evaluateLinkSafety({ isActive: true, warnAt: new Date() })).toBe('warn');
|
|
17
|
-
expect(evaluateLinkSafety({ isActive: true, warnAt: '2026-08-10T00:00:00Z' })).toBe('warn');
|
|
18
|
-
});
|
|
19
|
-
it('blocks when the owner is restricted, even if the link itself is fine', () => {
|
|
20
|
-
expect(evaluateLinkSafety({ isActive: true, ownerSuspendedAt: new Date() })).toBe('block');
|
|
21
|
-
});
|
|
22
|
-
it('prefers block over warn — a restricted owner outranks a mere warning', () => {
|
|
23
|
-
expect(evaluateLinkSafety({ isActive: true, warnAt: new Date(), ownerSuspendedAt: new Date() })).toBe('block');
|
|
24
|
-
});
|
|
25
|
-
it('ignores owner restriction when it is not modelled at all', () => {
|
|
26
|
-
// Deployments without an owner table never pass the field.
|
|
27
|
-
expect(evaluateLinkSafety({ isActive: true, ownerSuspendedAt: null })).toBe('allow');
|
|
28
|
-
expect(evaluateLinkSafety({ isActive: true, ownerSuspendedAt: undefined })).toBe('allow');
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('escapeHtml', () => {
|
|
32
|
-
it('neutralises markup and quote characters', () => {
|
|
33
|
-
expect(escapeHtml('<script>alert("x")</script>')).toBe('<script>alert("x")</script>');
|
|
34
|
-
expect(escapeHtml("it's & more")).toBe('it's & more');
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
describe('generateWarningLinkHTML', () => {
|
|
38
|
-
it('shows the destination so the visitor can judge it', () => {
|
|
39
|
-
const html = generateWarningLinkHTML('https://example.com/login');
|
|
40
|
-
expect(html).toContain('https://example.com/login');
|
|
41
|
-
expect(html).toContain('Check this link before continuing');
|
|
42
|
-
});
|
|
43
|
-
it('escapes a hostile destination rather than injecting it', () => {
|
|
44
|
-
const html = generateWarningLinkHTML('https://evil.test/"><script>alert(1)</script>');
|
|
45
|
-
expect(html).not.toContain('<script>alert(1)</script>');
|
|
46
|
-
expect(html).toContain('<script>');
|
|
47
|
-
});
|
|
48
|
-
it('asks search engines not to index it', () => {
|
|
49
|
-
expect(generateWarningLinkHTML('https://example.com')).toContain('noindex');
|
|
50
|
-
});
|
|
51
|
-
it('carries no JavaScript of its own, so it survives a strict CSP', () => {
|
|
52
|
-
const html = generateWarningLinkHTML('https://example.com');
|
|
53
|
-
expect(html).not.toContain('<script');
|
|
54
|
-
expect(html).not.toMatch(/\son[a-z]+=/i);
|
|
55
|
-
});
|
|
56
|
-
// The CSP case above used a benign destination, so it could never have failed for
|
|
57
|
-
// the reason it appeared to cover. These pass the hostile input instead.
|
|
58
|
-
describe('hostile destinations', () => {
|
|
59
|
-
it('never emits a javascript: href', () => {
|
|
60
|
-
const html = generateWarningLinkHTML('javascript:alert(document.domain)');
|
|
61
|
-
expect(html).not.toMatch(/href="javascript:/i);
|
|
62
|
-
});
|
|
63
|
-
it('never emits a data: href', () => {
|
|
64
|
-
const html = generateWarningLinkHTML('data:text/html,<script>alert(1)</script>');
|
|
65
|
-
expect(html).not.toMatch(/href="data:/i);
|
|
66
|
-
});
|
|
67
|
-
it('offers no continue button when the scheme is not http(s)', () => {
|
|
68
|
-
const html = generateWarningLinkHTML('javascript:alert(1)');
|
|
69
|
-
expect(html).not.toContain('class="go"');
|
|
70
|
-
expect(html).toContain('nothing to continue to');
|
|
71
|
-
});
|
|
72
|
-
it('still shows the destination as inert text so the visitor can see it', () => {
|
|
73
|
-
const html = generateWarningLinkHTML('javascript:alert(1)');
|
|
74
|
-
expect(html).toContain('javascript:alert(1)');
|
|
75
|
-
});
|
|
76
|
-
it('emits no empty href for a bare path or an absent destination', () => {
|
|
77
|
-
for (const d of ['/deep/link/path', '']) {
|
|
78
|
-
const html = generateWarningLinkHTML(d);
|
|
79
|
-
expect(html).not.toContain('href=""');
|
|
80
|
-
expect(html).not.toContain('class="go"');
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
it('still links a legitimate http(s) destination', () => {
|
|
84
|
-
const html = generateWarningLinkHTML('https://example.com/x');
|
|
85
|
-
expect(html).toContain('class="go"');
|
|
86
|
-
expect(html).toContain('href="https://example.com/x"');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
it('marks the outbound link nofollow/noopener so we pass no reputation to it', () => {
|
|
90
|
-
const html = generateWarningLinkHTML('https://example.com');
|
|
91
|
-
expect(html).toContain('rel="nofollow noopener noreferrer"');
|
|
92
|
-
});
|
|
93
|
-
it('includes a report link only when one is configured', () => {
|
|
94
|
-
expect(generateWarningLinkHTML('https://example.com')).not.toContain('Report this link');
|
|
95
|
-
const withReport = generateWarningLinkHTML('https://example.com', {
|
|
96
|
-
reportUrl: 'https://example.org/abuse',
|
|
97
|
-
});
|
|
98
|
-
expect(withReport).toContain('Report this link');
|
|
99
|
-
expect(withReport).toContain('https://example.org/abuse');
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe('safeHref', () => {
|
|
103
|
-
it('allows http and https', () => {
|
|
104
|
-
expect(safeHref('http://example.com')).toBe('http://example.com');
|
|
105
|
-
expect(safeHref('https://example.com/a?b=c')).toBe('https://example.com/a?b=c');
|
|
106
|
-
});
|
|
107
|
-
it('rejects every other scheme', () => {
|
|
108
|
-
for (const d of ['javascript:alert(1)', 'data:text/html,x', 'file:///etc/passwd', 'vbscript:x']) {
|
|
109
|
-
expect(safeHref(d), d).toBeNull();
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
it('rejects a relative path, which would point at the redirect host', () => {
|
|
113
|
-
expect(safeHref('/deep/link/path')).toBeNull();
|
|
114
|
-
expect(safeHref('')).toBeNull();
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
describe('evaluateLinkSafetyDecision — why a link was blocked', () => {
|
|
118
|
-
const T = '2026-08-10T00:00:00Z';
|
|
119
|
-
it('reports owner restriction as an abuse cause', () => {
|
|
120
|
-
const d = evaluateLinkSafetyDecision({ ownerSuspendedAt: T });
|
|
121
|
-
expect(d).toEqual({ outcome: 'block', cause: 'owner_suspended' });
|
|
122
|
-
expect(blockCauseIsAbuse(d.cause)).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
it('reports an explicit disable as an abuse cause', () => {
|
|
125
|
-
const d = evaluateLinkSafetyDecision({ isActive: false, disabledAt: T });
|
|
126
|
-
expect(d).toEqual({ outcome: 'block', cause: 'disabled' });
|
|
127
|
-
expect(blockCauseIsAbuse(d.cause)).toBe(true);
|
|
128
|
-
});
|
|
129
|
-
/**
|
|
130
|
-
* The distinction the whole notice rests on. An expiry sweep and a person
|
|
131
|
-
* switching their own link off both land here, and neither of their visitors was
|
|
132
|
-
* targeted by anything.
|
|
133
|
-
*/
|
|
134
|
-
it('reports a merely inactive link as NOT an abuse cause', () => {
|
|
135
|
-
const d = evaluateLinkSafetyDecision({ isActive: false });
|
|
136
|
-
expect(d).toEqual({ outcome: 'block', cause: 'inactive' });
|
|
137
|
-
expect(blockCauseIsAbuse(d.cause)).toBe(false);
|
|
138
|
-
});
|
|
139
|
-
/**
|
|
140
|
-
* Ordering guard. An abuse disable sets `isActive: false` as well, so testing
|
|
141
|
-
* `isActive` first would collapse every abuse disable into `inactive` and the
|
|
142
|
-
* notice would never be served to anyone. Every route test would still pass on
|
|
143
|
-
* the owner-restriction path, so this is the only place that catches it.
|
|
144
|
-
*/
|
|
145
|
-
it('keeps the disable cause even though the link is also inactive', () => {
|
|
146
|
-
expect(evaluateLinkSafetyDecision({ isActive: false, disabledAt: T }).cause).toBe('disabled');
|
|
147
|
-
});
|
|
148
|
-
it('lets owner restriction outrank an explicit disable', () => {
|
|
149
|
-
expect(evaluateLinkSafetyDecision({ ownerSuspendedAt: T, isActive: false, disabledAt: T }).cause).toBe('owner_suspended');
|
|
150
|
-
});
|
|
151
|
-
it('carries no cause when the link is fine or only warned', () => {
|
|
152
|
-
expect(evaluateLinkSafetyDecision({})).toEqual({ outcome: 'allow' });
|
|
153
|
-
expect(evaluateLinkSafetyDecision({ warnAt: T })).toEqual({ outcome: 'warn' });
|
|
154
|
-
});
|
|
155
|
-
it('agrees with evaluateLinkSafety on every outcome', () => {
|
|
156
|
-
for (const input of [
|
|
157
|
-
{},
|
|
158
|
-
{ warnAt: T },
|
|
159
|
-
{ isActive: false },
|
|
160
|
-
{ disabledAt: T },
|
|
161
|
-
{ ownerSuspendedAt: T },
|
|
162
|
-
{ warnAt: T, ownerSuspendedAt: T },
|
|
163
|
-
]) {
|
|
164
|
-
expect(evaluateLinkSafety(input)).toBe(evaluateLinkSafetyDecision(input).outcome);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
describe('generateBlockedLinkHTML', () => {
|
|
169
|
-
const html = generateBlockedLinkHTML();
|
|
170
|
-
it('says only that the link is gone', () => {
|
|
171
|
-
expect(html).toContain('This link is no longer available');
|
|
172
|
-
expect(html).toContain('It was removed and no longer goes anywhere.');
|
|
173
|
-
});
|
|
174
|
-
/**
|
|
175
|
-
* The guard on the decision, not on the wording.
|
|
176
|
-
*
|
|
177
|
-
* The page must not characterise the link, its destination, or whoever made it.
|
|
178
|
-
* An earlier draft explained that the link had been reported for trying to obtain
|
|
179
|
-
* passwords and payment details — useful to a victim, but it is an accusation
|
|
180
|
-
* about a user's content published on a domain we serve, and it would be false
|
|
181
|
-
* the first time a link is disabled in error. If any of these words come back,
|
|
182
|
-
* that decision is being reversed and it should be a deliberate edit here first.
|
|
183
|
-
*/
|
|
184
|
-
it('makes no claim about the link or whoever created it', () => {
|
|
185
|
-
for (const word of [
|
|
186
|
-
'phishing', 'fraud', 'fraudulent', 'scam', 'malicious', 'malware', 'abuse',
|
|
187
|
-
'reported', 'trick', 'steal', 'suspended', 'restricted', 'violat', 'blocked',
|
|
188
|
-
'password', 'bank', 'card details',
|
|
189
|
-
]) {
|
|
190
|
-
expect(html.toLowerCase(), `page must not mention "${word}"`).not.toContain(word);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
it('contains no link out at all', () => {
|
|
194
|
-
expect(html).not.toMatch(/<a\s/i);
|
|
195
|
-
});
|
|
196
|
-
it('contains no script and no external asset', () => {
|
|
197
|
-
expect(html).not.toMatch(/<script/i);
|
|
198
|
-
expect(html).not.toMatch(/https?:\/\//);
|
|
199
|
-
});
|
|
200
|
-
it('asks not to be indexed', () => {
|
|
201
|
-
expect(html).toContain('noindex');
|
|
202
|
-
});
|
|
203
|
-
/**
|
|
204
|
-
* Arity is the real containment. Adding a parameter is how a destination or an
|
|
205
|
-
* owner ends up rendered to a stranger, so it fails here before it fails in review.
|
|
206
|
-
*/
|
|
207
|
-
it('takes no input, so it cannot carry anything from the link', () => {
|
|
208
|
-
expect(generateBlockedLinkHTML.length).toBe(0);
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
//# sourceMappingURL=link-safety.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-safety.test.js","sourceRoot":"","sources":["../../src/lib/link-safety.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"not-found-page.test.d.ts","sourceRoot":"","sources":["../../src/lib/not-found-page.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { NOT_FOUND_DEFAULTS, NOT_FOUND_SETTINGS_TTL_MS, createOrganizationNotFoundSettingsLookup, readNotFoundSettings, readOrganizationNotFoundSettings, resolveNotFoundSettings, notFoundContentSecurityPolicy, renderNotFoundPage, safeNotFoundRedirect, wantsHtml, } from './not-found-page.js';
|
|
3
|
-
describe('wantsHtml', () => {
|
|
4
|
-
it('is true for a browser navigation', () => {
|
|
5
|
-
expect(wantsHtml('text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')).toBe(true);
|
|
6
|
-
});
|
|
7
|
-
it('is false for JSON clients, wildcards and a missing header', () => {
|
|
8
|
-
expect(wantsHtml('application/json')).toBe(false);
|
|
9
|
-
expect(wantsHtml('*/*')).toBe(false);
|
|
10
|
-
expect(wantsHtml(undefined)).toBe(false);
|
|
11
|
-
});
|
|
12
|
-
it('reads a repeated header', () => {
|
|
13
|
-
expect(wantsHtml(['application/json', 'text/html'])).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
describe('renderNotFoundPage', () => {
|
|
17
|
-
it('renders the default copy with no content', () => {
|
|
18
|
-
const html = renderNotFoundPage();
|
|
19
|
-
expect(html).toContain(`<title>${NOT_FOUND_DEFAULTS.title}</title>`);
|
|
20
|
-
expect(html).toContain('doesn't exist or is no longer available.');
|
|
21
|
-
expect(html).not.toContain('<a ');
|
|
22
|
-
expect(html).not.toContain('<img');
|
|
23
|
-
});
|
|
24
|
-
it('runs no script', () => {
|
|
25
|
-
expect(renderNotFoundPage({ title: 'x' })).not.toContain('<script');
|
|
26
|
-
expect(notFoundContentSecurityPolicy()).not.toContain('script-src');
|
|
27
|
-
expect(notFoundContentSecurityPolicy()).toContain("default-src 'none'");
|
|
28
|
-
});
|
|
29
|
-
it('renders host content', () => {
|
|
30
|
-
const html = renderNotFoundPage({
|
|
31
|
-
title: 'Nothing here',
|
|
32
|
-
message: 'Try our homepage.',
|
|
33
|
-
logoUrl: 'https://cdn.example.com/logo.png',
|
|
34
|
-
accentColor: '#0d9488',
|
|
35
|
-
backgroundColor: '#fafafa',
|
|
36
|
-
buttonLabel: 'Go home',
|
|
37
|
-
buttonUrl: 'https://example.com/',
|
|
38
|
-
});
|
|
39
|
-
expect(html).toContain('<h1>Nothing here</h1>');
|
|
40
|
-
expect(html).toContain('<p>Try our homepage.</p>');
|
|
41
|
-
expect(html).toContain('src="https://cdn.example.com/logo.png"');
|
|
42
|
-
expect(html).toContain('href="https://example.com/"');
|
|
43
|
-
expect(html).toContain('>Go home</a>');
|
|
44
|
-
expect(html).toContain('background:#0d9488');
|
|
45
|
-
expect(html).toContain('background:#fafafa');
|
|
46
|
-
});
|
|
47
|
-
it('applies the brand background in light mode only', () => {
|
|
48
|
-
const html = renderNotFoundPage({ backgroundColor: '#ecfdf5' });
|
|
49
|
-
expect(html).toContain('@media (prefers-color-scheme: light) { body { background:#ecfdf5; } }');
|
|
50
|
-
expect(html.match(/background:#ecfdf5/g)).toHaveLength(1);
|
|
51
|
-
});
|
|
52
|
-
it('escapes host copy', () => {
|
|
53
|
-
const html = renderNotFoundPage({ title: '<script>alert(1)</script>', message: '"><img src=x>' });
|
|
54
|
-
expect(html).not.toContain('<script>alert(1)');
|
|
55
|
-
expect(html).toContain('<script>alert(1)</script>');
|
|
56
|
-
expect(html).not.toContain('"><img src=x>');
|
|
57
|
-
});
|
|
58
|
-
it('drops non-http URLs', () => {
|
|
59
|
-
const html = renderNotFoundPage({
|
|
60
|
-
logoUrl: 'javascript:alert(1)',
|
|
61
|
-
buttonLabel: 'Click',
|
|
62
|
-
buttonUrl: 'javascript:alert(1)',
|
|
63
|
-
});
|
|
64
|
-
expect(html).not.toContain('javascript:');
|
|
65
|
-
expect(html).not.toContain('<a ');
|
|
66
|
-
expect(html).not.toContain('<img');
|
|
67
|
-
});
|
|
68
|
-
it('needs both a label and a URL for the button', () => {
|
|
69
|
-
expect(renderNotFoundPage({ buttonUrl: 'https://example.com/' })).not.toContain('<a ');
|
|
70
|
-
expect(renderNotFoundPage({ buttonLabel: 'Go' })).not.toContain('<a ');
|
|
71
|
-
});
|
|
72
|
-
it('drops colors that are not hex', () => {
|
|
73
|
-
const html = renderNotFoundPage({ accentColor: 'red;}body{display:none', backgroundColor: 'url(x)' });
|
|
74
|
-
expect(html).not.toContain('display:none');
|
|
75
|
-
expect(html).not.toContain('url(x)');
|
|
76
|
-
});
|
|
77
|
-
it('treats blank copy as absent', () => {
|
|
78
|
-
expect(renderNotFoundPage({ title: ' ' })).toContain(`<h1>${NOT_FOUND_DEFAULTS.title}</h1>`);
|
|
79
|
-
});
|
|
80
|
-
it('caps long copy', () => {
|
|
81
|
-
const html = renderNotFoundPage({ title: 'x'.repeat(1000) });
|
|
82
|
-
expect(html).not.toContain('x'.repeat(121));
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
describe('safeNotFoundRedirect', () => {
|
|
86
|
-
it('accepts http(s)', () => {
|
|
87
|
-
expect(safeNotFoundRedirect('https://example.com/a?b=1')).toBe('https://example.com/a?b=1');
|
|
88
|
-
});
|
|
89
|
-
it('rejects everything else', () => {
|
|
90
|
-
expect(safeNotFoundRedirect('javascript:alert(1)')).toBeNull();
|
|
91
|
-
expect(safeNotFoundRedirect('/relative')).toBeNull();
|
|
92
|
-
expect(safeNotFoundRedirect(undefined)).toBeNull();
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
describe('readNotFoundSettings', () => {
|
|
96
|
-
it('keeps known, non-empty string fields', () => {
|
|
97
|
-
expect(readNotFoundSettings({ title: ' Hi ', message: '', logoUrl: 42, extra: 'x', redirectUrl: 'https://a.example' }))
|
|
98
|
-
.toEqual({ title: 'Hi', redirectUrl: 'https://a.example' });
|
|
99
|
-
});
|
|
100
|
-
it('is null when nothing is set', () => {
|
|
101
|
-
expect(readNotFoundSettings({})).toBeNull();
|
|
102
|
-
expect(readNotFoundSettings(null)).toBeNull();
|
|
103
|
-
expect(readNotFoundSettings(['title'])).toBeNull();
|
|
104
|
-
});
|
|
105
|
-
it('reads an organization settings column', () => {
|
|
106
|
-
expect(readOrganizationNotFoundSettings({ notFound: { title: 'Org' }, launchpad: {} })).toEqual({ title: 'Org' });
|
|
107
|
-
expect(readOrganizationNotFoundSettings({ launchpad: {} })).toBeNull();
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
describe('resolveNotFoundSettings', () => {
|
|
111
|
-
it('redirects when the settings name a valid URL', () => {
|
|
112
|
-
expect(resolveNotFoundSettings({ redirectUrl: 'https://example.com/', title: 'x' }))
|
|
113
|
-
.toEqual({ kind: 'redirect', url: 'https://example.com/' });
|
|
114
|
-
});
|
|
115
|
-
it('renders the page when the redirect is not http(s)', () => {
|
|
116
|
-
expect(resolveNotFoundSettings({ redirectUrl: 'javascript:alert(1)', title: 'x' }))
|
|
117
|
-
.toEqual({ kind: 'page', content: { title: 'x' } });
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
describe('createOrganizationNotFoundSettingsLookup', () => {
|
|
121
|
-
it('holds an answer for the TTL, then reads again', async () => {
|
|
122
|
-
let clock = 0;
|
|
123
|
-
const query = vi.fn(async () => ({ rows: [{ settings: { notFound: { title: 'Org' } } }] }));
|
|
124
|
-
const lookup = createOrganizationNotFoundSettingsLookup({ query, now: () => clock });
|
|
125
|
-
expect(await lookup('org-a')).toEqual({ title: 'Org' });
|
|
126
|
-
await lookup('org-a');
|
|
127
|
-
expect(query).toHaveBeenCalledTimes(1);
|
|
128
|
-
clock += NOT_FOUND_SETTINGS_TTL_MS + 1;
|
|
129
|
-
await lookup('org-a');
|
|
130
|
-
expect(query).toHaveBeenCalledTimes(2);
|
|
131
|
-
});
|
|
132
|
-
it('holds a "no settings" answer too', async () => {
|
|
133
|
-
const query = vi.fn(async () => ({ rows: [] }));
|
|
134
|
-
const lookup = createOrganizationNotFoundSettingsLookup({ query });
|
|
135
|
-
expect(await lookup('org-a')).toBeNull();
|
|
136
|
-
await lookup('org-a');
|
|
137
|
-
expect(query).toHaveBeenCalledTimes(1);
|
|
138
|
-
});
|
|
139
|
-
it('does not hold a failed read', async () => {
|
|
140
|
-
const onError = vi.fn();
|
|
141
|
-
const query = vi
|
|
142
|
-
.fn()
|
|
143
|
-
.mockRejectedValueOnce(new Error('db down'))
|
|
144
|
-
.mockResolvedValueOnce({ rows: [{ settings: { notFound: { title: 'Org' } } }] });
|
|
145
|
-
const lookup = createOrganizationNotFoundSettingsLookup({ query, onError });
|
|
146
|
-
expect(await lookup('org-a')).toBeNull();
|
|
147
|
-
expect(onError).toHaveBeenCalledOnce();
|
|
148
|
-
expect(await lookup('org-a')).toEqual({ title: 'Org' });
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
//# sourceMappingURL=not-found-page.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"not-found-page.test.js","sourceRoot":"","sources":["../../src/lib/not-found-page.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../../src/lib/portable/cli.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import zlib from 'zlib';
|
|
5
|
-
import pg from 'pg';
|
|
6
|
-
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
7
|
-
import { runCli } from './cli.js';
|
|
8
|
-
/**
|
|
9
|
-
* The `linkforty` command: arguments, exit codes and what it prints. The
|
|
10
|
-
* export and import runs need a real database and skip without DATABASE_URL.
|
|
11
|
-
*/
|
|
12
|
-
function capture() {
|
|
13
|
-
const lines = [];
|
|
14
|
-
const errors = [];
|
|
15
|
-
return { lines, errors, log: (l) => lines.push(l), logError: (l) => errors.push(l) };
|
|
16
|
-
}
|
|
17
|
-
describe('linkforty usage', () => {
|
|
18
|
-
it('prints usage and exits 2 for an unknown command or a missing file', async () => {
|
|
19
|
-
for (const argv of [[], ['backup'], ['export']]) {
|
|
20
|
-
const out = capture();
|
|
21
|
-
expect(await runCli(argv, {}, out.log, out.logError)).toBe(2);
|
|
22
|
-
expect(out.errors.join('\n')).toContain('Usage:');
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
it('exits 2 for a malformed flag', async () => {
|
|
26
|
-
const out = capture();
|
|
27
|
-
expect(await runCli(['import', 'x.lfbundle', '--organization'], {}, out.log, out.logError)).toBe(2);
|
|
28
|
-
expect(out.errors[0]).toContain('--organization needs a value');
|
|
29
|
-
});
|
|
30
|
-
it('exits 2 without DATABASE_URL', async () => {
|
|
31
|
-
const out = capture();
|
|
32
|
-
expect(await runCli(['export', 'x.lfbundle'], {}, out.log, out.logError)).toBe(2);
|
|
33
|
-
expect(out.errors[0]).toContain('DATABASE_URL');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
const ADMIN_URL = process.env.DATABASE_URL;
|
|
37
|
-
describe.skipIf(!ADMIN_URL)('linkforty export and import (integration)', () => {
|
|
38
|
-
const stamp = Date.now();
|
|
39
|
-
const names = { source: `lf_core_cli_src_${stamp}`, target: `lf_core_cli_dst_${stamp}` };
|
|
40
|
-
let admin;
|
|
41
|
-
let dir;
|
|
42
|
-
let file;
|
|
43
|
-
const urlFor = (name) => {
|
|
44
|
-
const url = new URL(ADMIN_URL);
|
|
45
|
-
url.pathname = `/${name}`;
|
|
46
|
-
return url.toString();
|
|
47
|
-
};
|
|
48
|
-
const run = async (argv, database) => {
|
|
49
|
-
const out = capture();
|
|
50
|
-
const code = await runCli(argv, { DATABASE_URL: urlFor(names[database]) }, out.log, out.logError);
|
|
51
|
-
return { code, text: out.lines.join('\n'), errors: out.errors.join('\n') };
|
|
52
|
-
};
|
|
53
|
-
beforeAll(async () => {
|
|
54
|
-
admin = new pg.Client({ connectionString: ADMIN_URL });
|
|
55
|
-
await admin.connect();
|
|
56
|
-
for (const name of Object.values(names))
|
|
57
|
-
await admin.query(`CREATE DATABASE ${name}`);
|
|
58
|
-
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'lf-cli-'));
|
|
59
|
-
file = path.join(dir, 'server.lfbundle');
|
|
60
|
-
// An export creates the schema; then seed one link and one webhook.
|
|
61
|
-
await run(['export', path.join(dir, 'empty.lfbundle')], 'source');
|
|
62
|
-
const source = new pg.Client({ connectionString: urlFor(names.source) });
|
|
63
|
-
await source.connect();
|
|
64
|
-
await source.query(`INSERT INTO links (short_code, original_url) VALUES ('hello', 'https://example.com')`);
|
|
65
|
-
await source.query(`INSERT INTO webhooks (name, url, secret, events) VALUES ('w', 'https://hooks.example', 's', '{click_event}')`);
|
|
66
|
-
await source.end();
|
|
67
|
-
}, 60000);
|
|
68
|
-
afterAll(async () => {
|
|
69
|
-
for (const name of Object.values(names))
|
|
70
|
-
await admin.query(`DROP DATABASE IF EXISTS ${name} WITH (FORCE)`);
|
|
71
|
-
await admin.end();
|
|
72
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
73
|
-
}, 30000);
|
|
74
|
-
it('exports, dry-runs, imports and then reports conflicts', async () => {
|
|
75
|
-
const exported = await run(['export', file], 'source');
|
|
76
|
-
expect(exported.code).toBe(0);
|
|
77
|
-
expect(exported.text).toContain('Exported 2 rows');
|
|
78
|
-
const dry = await run(['import', file, '--dry-run'], 'target');
|
|
79
|
-
expect(dry).toMatchObject({ code: 0 });
|
|
80
|
-
expect(dry.text).toContain('Dry run: no conflicts');
|
|
81
|
-
const imported = await run(['import', file], 'target');
|
|
82
|
-
expect(imported.code).toBe(0);
|
|
83
|
-
expect(imported.text).toContain('Imported.');
|
|
84
|
-
expect(imported.text).toMatch(/https:\/\/hooks\.example {2}[0-9a-f]{64}/);
|
|
85
|
-
const again = await run(['import', file], 'target');
|
|
86
|
-
expect(again.code).toBe(1);
|
|
87
|
-
expect(again.text).toContain('conflict(s); nothing was imported');
|
|
88
|
-
expect(again.text).toContain('short_code_taken: hello');
|
|
89
|
-
}, 60000);
|
|
90
|
-
it('exits 1 with the reason for a file that is not a bundle', async () => {
|
|
91
|
-
const bogus = path.join(dir, 'bogus.lfbundle');
|
|
92
|
-
fs.writeFileSync(bogus, zlib.gzipSync('{"hello":"world"}\n'));
|
|
93
|
-
const result = await run(['import', bogus], 'target');
|
|
94
|
-
expect(result.code).toBe(1);
|
|
95
|
-
expect(result.errors).toContain('Not a LinkForty bundle');
|
|
96
|
-
}, 60000);
|
|
97
|
-
});
|
|
98
|
-
//# sourceMappingURL=cli.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../../../src/lib/portable/cli.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"portable.integration.test.d.ts","sourceRoot":"","sources":["../../../src/lib/portable/portable.integration.test.ts"],"names":[],"mappings":""}
|