safe-link-checker 1.0.0 → 1.1.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/README.md +68 -50
- package/dist/browser/index.cjs +1 -0
- package/dist/browser/index.d.cts +258 -0
- package/dist/browser/index.d.ts +258 -0
- package/dist/browser/index.js +1 -0
- package/dist/chunk-DDF3IZSI.js +1 -0
- package/dist/chunk-K5U3MVS5.cjs +1 -0
- package/dist/chunk-LWMILG2I.js +119 -0
- package/dist/chunk-XNJKJHJI.cjs +119 -0
- package/dist/edge/index.cjs +1 -0
- package/dist/edge/index.d.cts +26 -0
- package/dist/edge/index.d.ts +26 -0
- package/dist/edge/index.js +1 -0
- package/dist/node/index.cjs +2 -0
- package/dist/node/index.d.cts +26 -0
- package/dist/node/index.d.ts +26 -0
- package/dist/node/index.js +2 -0
- package/package.json +96 -53
- package/CHANGELOG.md +0 -25
- package/CONTRIBUTING.md +0 -39
- package/SECURITY.md +0 -20
- package/dist/chunk-CS7EDB5I.js +0 -1806
- package/dist/chunk-CS7EDB5I.js.map +0 -1
- package/dist/cli.cjs +0 -1899
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -83
- package/dist/cli.js.map +0 -1
- package/dist/index.cjs +0 -1986
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -497
- package/dist/index.d.ts +0 -497
- package/dist/index.js +0 -156
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,83 +2,101 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://npmjs.org/package/safe-link-checker)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/your-username/safe-link-checker/actions)
|
|
6
|
-
[](#)
|
|
7
5
|
|
|
8
|
-
An enterprise-grade, lightning-fast
|
|
6
|
+
An enterprise-grade, lightning-fast **Universal URL Intelligence SDK**. `SafeLinkChecker` protects against phishing, malware, SSRF bypasses, DNS rebinding, and malicious payloads. It works seamlessly across Node.js, React Native, Browser, Next.js, Electron, Bun, and Deno with a unified API.
|
|
9
7
|
|
|
10
8
|
## Features ✨
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **Dual Build**: Fully tree-shakable ESM and CJS exports.
|
|
9
|
+
- **Write Once, Run Anywhere**: Automatically detects the runtime environment to serve the best implementation.
|
|
10
|
+
- **Frontend Verification**: React Native, Expo, and Web Apps execute fast local checks (URL extraction, punycode, homographs, regex rules) in <10ms.
|
|
11
|
+
- **Backend Verification**: Node.js/Bun servers enrich analysis with deep network checks (DNS rebinding, HTTPS certificates, redirect tracing) and Threat Intelligence Providers (URLHaus, OpenPhish).
|
|
12
|
+
- **Consensus & Policy Engine**: Configurable policies calculate trust scores and decide actions (`allow`, `warn`, `block`).
|
|
16
13
|
|
|
17
14
|
## Installation 📦
|
|
18
15
|
|
|
19
16
|
```bash
|
|
20
17
|
npm install safe-link-checker
|
|
21
|
-
# or
|
|
22
|
-
yarn add safe-link-checker
|
|
23
|
-
# or
|
|
24
|
-
pnpm add safe-link-checker
|
|
25
18
|
```
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
> **One package.** Installs the correct bundle for your platform automatically.
|
|
20
|
+
|
|
21
|
+
## Compatibility Matrix 📊
|
|
22
|
+
|
|
23
|
+
| Feature \ Platform | Node.js / Bun | React Native / Expo | Browser / Web |
|
|
24
|
+
|:---|:---:|:---:|:---:|
|
|
25
|
+
| URL Normalization | ✅ | ✅ | ✅ |
|
|
26
|
+
| URL Extraction | ✅ | ✅ | ✅ |
|
|
27
|
+
| Regex / Heuristics | ✅ | ✅ | ✅ |
|
|
28
|
+
| Homograph / Punycode| ✅ | ✅ | ✅ |
|
|
29
|
+
| Private IP / SSRF | ✅ | ✅ | ✅ |
|
|
30
|
+
| Explainable Scoring | ✅ | ✅ | ✅ |
|
|
31
|
+
| Cache Engine | ✅ | ✅ | ✅ |
|
|
32
|
+
| DNS Lookup | ✅ | ❌ | ❌ |
|
|
33
|
+
| HTTPS Certificate | ✅ | ❌ | ❌ |
|
|
34
|
+
| Redirect Tracing | ✅ | ❌ | ❌ |
|
|
35
|
+
| Threat Providers | ✅ | ❌ | ❌ |
|
|
28
36
|
|
|
29
37
|
## Quick Start 🚀
|
|
30
38
|
|
|
39
|
+
### In Node.js / Backend
|
|
40
|
+
|
|
31
41
|
```typescript
|
|
32
|
-
import { SafeLinkChecker } from 'safe-link-checker';
|
|
42
|
+
import { SafeLinkChecker, verifyLink } from 'safe-link-checker';
|
|
33
43
|
|
|
44
|
+
// Uses Node.js networking, DNS, and Threat Intelligence automatically
|
|
34
45
|
const checker = new SafeLinkChecker({
|
|
35
46
|
providers: ['urlhaus', 'openphish'],
|
|
36
47
|
cache: true,
|
|
37
|
-
|
|
48
|
+
checkHttps: true,
|
|
38
49
|
});
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
console.log(`Threat Score: ${result.score}/100`);
|
|
45
|
-
|
|
46
|
-
if (!result.safe) {
|
|
47
|
-
console.log(`Reasons: ${result.reasons.join(', ')}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
run();
|
|
51
|
+
const result = await checker.verify('https://example.com');
|
|
52
|
+
console.log(result.runtime); // 'node'
|
|
53
|
+
console.log(result.decision); // 'allow'
|
|
54
|
+
console.log(result.capabilities.performed); // ['UrlValidation', 'HttpsValidation', ...]
|
|
52
55
|
```
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
You can verify massive lists of URLs concurrently. The engine automatically handles concurrency limits and caches results.
|
|
57
|
+
### In React Native / Browser (e.g. Chat Composer)
|
|
57
58
|
|
|
58
59
|
```typescript
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
60
|
+
import { SafeLinkChecker, extractUrls } from 'safe-link-checker';
|
|
61
|
+
|
|
62
|
+
// Fast local execution - blocks obvious threats before they are sent
|
|
63
|
+
const checker = new SafeLinkChecker();
|
|
64
|
+
|
|
65
|
+
const text = "Check this out: http://google.com and http://evil.com/exe";
|
|
66
|
+
const urls = extractUrls(text);
|
|
67
|
+
|
|
68
|
+
const results = await checker.verifyLinks(urls);
|
|
69
|
+
const blockedUrls = results.filter(r => r.decision === 'block');
|
|
70
|
+
|
|
71
|
+
if (blockedUrls.length > 0) {
|
|
72
|
+
alert('Malicious link detected! Please remove before sending.');
|
|
73
|
+
}
|
|
67
74
|
```
|
|
68
75
|
|
|
69
|
-
##
|
|
76
|
+
## Result Model
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
- `Plugins` (e.g., `UrlValidation`, `IpValidation`, `PunycodePlugin`) independently analyze a URL and emit a `CheckResult` with a `scoreImpact`.
|
|
73
|
-
- The `ConsensusEngine` aggregates these scores. A score >= 50 triggers a fatal abort.
|
|
74
|
-
- `Providers` (e.g., URLHaus, OpenPhish) hit cloud intelligence feeds.
|
|
78
|
+
Both runtimes return the exact same structured result format:
|
|
75
79
|
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
```typescript
|
|
81
|
+
{
|
|
82
|
+
url: 'https://evil.com',
|
|
83
|
+
safe: false,
|
|
84
|
+
decision: 'block',
|
|
85
|
+
score: 100,
|
|
86
|
+
confidence: 90,
|
|
87
|
+
riskLevel: 'DANGEROUS',
|
|
88
|
+
summary: 'Detected suspicious patterns.',
|
|
89
|
+
action: 'block',
|
|
90
|
+
runtime: 'react-native',
|
|
91
|
+
capabilities: {
|
|
92
|
+
performed: ['UrlValidation', 'Punycode', 'Heuristics'],
|
|
93
|
+
skipped: ['dns', 'certificate', 'redirect_trace']
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
78
97
|
|
|
79
|
-
##
|
|
80
|
-
|
|
81
|
-
See the [Contributing Guidelines](CONTRIBUTING.md) to get started.
|
|
98
|
+
## Security 🔒
|
|
99
|
+
Please review our [Security Policy](SECURITY.md) for reporting vulnerabilities.
|
|
82
100
|
|
|
83
101
|
## License 📄
|
|
84
|
-
[MIT](LICENSE) © 2026
|
|
102
|
+
[MIT](LICENSE) © 2026 Rajeev Choudhary
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var chunkK5U3MVS5_cjs=require('../chunk-K5U3MVS5.cjs'),chunkXNJKJHJI_cjs=require('../chunk-XNJKJHJI.cjs');Object.defineProperty(exports,"SafeLinkChecker",{enumerable:true,get:function(){return chunkK5U3MVS5_cjs.c}});Object.defineProperty(exports,"SafeLinkError",{enumerable:true,get:function(){return chunkK5U3MVS5_cjs.a}});Object.defineProperty(exports,"TimeoutError",{enumerable:true,get:function(){return chunkK5U3MVS5_cjs.b}});Object.defineProperty(exports,"extractUrls",{enumerable:true,get:function(){return chunkK5U3MVS5_cjs.f}});Object.defineProperty(exports,"verifyLink",{enumerable:true,get:function(){return chunkK5U3MVS5_cjs.d}});Object.defineProperty(exports,"verifyLinks",{enumerable:true,get:function(){return chunkK5U3MVS5_cjs.e}});Object.defineProperty(exports,"AnalyticsTracker",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.o}});Object.defineProperty(exports,"CloudGateway",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.r}});Object.defineProperty(exports,"ConsensusEngine",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.f}});Object.defineProperty(exports,"DefaultPluginFactory",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.d}});Object.defineProperty(exports,"EventEmitter",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.c}});Object.defineProperty(exports,"LRUCache",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.a}});Object.defineProperty(exports,"MemoryCache",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.a}});Object.defineProperty(exports,"PluginManager",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.e}});Object.defineProperty(exports,"PolicyEngine",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.g}});Object.defineProperty(exports,"RealtimeSubscriptionEngine",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.p}});Object.defineProperty(exports,"ReputationEngine",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.q}});Object.defineProperty(exports,"RuleEnginePlugin",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.h}});Object.defineProperty(exports,"createSecurityReport",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.t}});Object.defineProperty(exports,"defaultCache",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.b}});Object.defineProperty(exports,"formatResult",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.s}});Object.defineProperty(exports,"injectReportHelpers",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.u}});Object.defineProperty(exports,"normalizeLink",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.i}});Object.defineProperty(exports,"validateHeuristics",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.n}});Object.defineProperty(exports,"validateIp",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.k}});Object.defineProperty(exports,"validatePunycode",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.l}});Object.defineProperty(exports,"validateShortener",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.m}});Object.defineProperty(exports,"validateUrl",{enumerable:true,get:function(){return chunkXNJKJHJI_cjs.j}});
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
export { MemoryCache, defaultCache } from '@safe-link-checker/cache/memory.js';
|
|
2
|
+
export { LRUCache } from '@safe-link-checker/cache/lru.js';
|
|
3
|
+
export { EventEmitter } from './core/events.js';
|
|
4
|
+
export { DefaultPluginFactory } from './core/factory.js';
|
|
5
|
+
export { PluginContext, PluginManager, PluginType, VerificationPlugin } from './core/plugin.js';
|
|
6
|
+
export { ConsensusEngine } from './engine/consensus.js';
|
|
7
|
+
export { PolicyEngine } from './engine/policy.js';
|
|
8
|
+
export { RuleEnginePlugin } from './engine/rules.js';
|
|
9
|
+
export { normalizeLink } from './utils/normalize.js';
|
|
10
|
+
export { validateUrl } from './validators/url.js';
|
|
11
|
+
export { validateIp } from './validators/ip.js';
|
|
12
|
+
export { validatePunycode } from './validators/punycode.js';
|
|
13
|
+
export { validateShortener } from './validators/shortener.js';
|
|
14
|
+
export { validateHeuristics } from './validators/heuristic.js';
|
|
15
|
+
export { AnalyticsTracker } from './analytics/tracker.js';
|
|
16
|
+
export { RealtimeSubscriptionEngine } from './realtime/subscription.js';
|
|
17
|
+
export { ReputationEngine, ReputationResult } from './engine/reputation.js';
|
|
18
|
+
export { CloudGateway } from './cloud/gateway.js';
|
|
19
|
+
export { formatResult } from './utils/formatter.js';
|
|
20
|
+
export { ReportData, createSecurityReport, injectReportHelpers } from './utils/report.js';
|
|
21
|
+
export { extractUrls, verifyLink, verifyLinks } from './verify.js';
|
|
22
|
+
export { CheckerOptions, SafeLinkChecker, SafeLinkError, TimeoutError } from './checker.js';
|
|
23
|
+
import './base.js';
|
|
24
|
+
import './validators/https.js';
|
|
25
|
+
import './validators/redirect.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* SafeLinkChecker
|
|
29
|
+
* Copyright (c) 2026
|
|
30
|
+
*
|
|
31
|
+
* This source code is licensed under the MIT license found in the
|
|
32
|
+
* LICENSE file in the root directory of this source tree.
|
|
33
|
+
*/
|
|
34
|
+
type RiskLevel = 'SAFE' | 'SUSPICIOUS' | 'DANGEROUS';
|
|
35
|
+
type HttpsStatus = 'HTTPS' | 'HTTP_ONLY' | 'CERT_ERROR' | 'TIMEOUT' | 'UNREACHABLE' | 'SKIPPED';
|
|
36
|
+
type RedirectAnomalyKind = 'LOOP' | 'PROTOCOL_DOWNGRADE' | 'MAX_REDIRECTS_EXCEEDED';
|
|
37
|
+
type RiskCategory = 'domain' | 'certificate' | 'redirect' | 'content' | 'network' | 'provider' | 'browser' | 'email' | 'qr' | 'download' | 'behavior' | 'ai' | 'other';
|
|
38
|
+
type RiskSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
39
|
+
type DecisionAction = 'allow' | 'warn' | 'block' | 'unknown' | 'ALLOW' | 'WARN' | 'REVIEW' | 'BLOCK' | 'ESCALATE';
|
|
40
|
+
type Classification = 'trusted' | 'safe' | 'suspicious' | 'unknown' | 'tracking' | 'spam' | 'phishing' | 'malware' | 'credential_theft' | 'scam' | 'brand_impersonation' | 'typosquatting' | 'Safe' | 'Suspicious' | 'Phishing' | 'Malware' | 'Scam' | 'Typosquatting' | 'Brand Impersonation' | 'Tracking' | 'Unsafe' | 'Unknown';
|
|
41
|
+
type ThreatLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' | 'UNKNOWN';
|
|
42
|
+
interface ObservabilityHooks {
|
|
43
|
+
onStart?: (url: string, options: VerifyOptions) => void;
|
|
44
|
+
onFinish?: (result: VerificationResult) => void;
|
|
45
|
+
onWarning?: (url: string, reasons: string[]) => void;
|
|
46
|
+
onBlocked?: (url: string, reasons: string[]) => void;
|
|
47
|
+
onProvider?: (providerName: string, result: CheckResult) => void;
|
|
48
|
+
onCacheHit?: (url: string) => void;
|
|
49
|
+
onCacheMiss?: (url: string) => void;
|
|
50
|
+
}
|
|
51
|
+
interface VerifyOptions {
|
|
52
|
+
maxRedirects?: number;
|
|
53
|
+
timeout?: number;
|
|
54
|
+
customShorteners?: string[];
|
|
55
|
+
bypassCache?: boolean;
|
|
56
|
+
removeTrackingParams?: boolean;
|
|
57
|
+
checkHttps?: boolean;
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
policy?: string;
|
|
60
|
+
hooks?: ObservabilityHooks;
|
|
61
|
+
realtime?: boolean;
|
|
62
|
+
cloud?: {
|
|
63
|
+
enabled: boolean;
|
|
64
|
+
apiKey: string;
|
|
65
|
+
endpoint?: string;
|
|
66
|
+
};
|
|
67
|
+
telemetry?: {
|
|
68
|
+
enabled: boolean;
|
|
69
|
+
level?: 'anonymous' | 'full';
|
|
70
|
+
};
|
|
71
|
+
debug?: boolean;
|
|
72
|
+
}
|
|
73
|
+
interface CheckResult {
|
|
74
|
+
name: string;
|
|
75
|
+
safe: boolean;
|
|
76
|
+
scoreImpact: number;
|
|
77
|
+
message: string;
|
|
78
|
+
weight?: number;
|
|
79
|
+
fatal?: boolean;
|
|
80
|
+
detector?: string;
|
|
81
|
+
category?: RiskCategory;
|
|
82
|
+
severity?: RiskSeverity;
|
|
83
|
+
confidence?: number;
|
|
84
|
+
scoreContribution?: number;
|
|
85
|
+
title?: string;
|
|
86
|
+
description?: string;
|
|
87
|
+
recommendation?: string;
|
|
88
|
+
references?: string[];
|
|
89
|
+
executionTime?: number;
|
|
90
|
+
timestamp?: number;
|
|
91
|
+
metadata?: Record<string, unknown>;
|
|
92
|
+
}
|
|
93
|
+
interface Provider {
|
|
94
|
+
name: string;
|
|
95
|
+
check(url: string, options?: VerifyOptions): Promise<CheckResult | null>;
|
|
96
|
+
}
|
|
97
|
+
interface RedirectHop {
|
|
98
|
+
url: string;
|
|
99
|
+
statusCode: number;
|
|
100
|
+
}
|
|
101
|
+
interface RedirectTrace {
|
|
102
|
+
chain: string[];
|
|
103
|
+
finalUrl: string;
|
|
104
|
+
redirectCount: number;
|
|
105
|
+
anomalies: RedirectAnomalyKind[];
|
|
106
|
+
}
|
|
107
|
+
interface ExecutionTimeline {
|
|
108
|
+
phase: string;
|
|
109
|
+
startTime: number;
|
|
110
|
+
durationMs: number;
|
|
111
|
+
status: 'success' | 'error' | 'skipped';
|
|
112
|
+
}
|
|
113
|
+
interface RichMetadata {
|
|
114
|
+
favicon?: string;
|
|
115
|
+
title?: string;
|
|
116
|
+
description?: string;
|
|
117
|
+
openGraph?: Record<string, string>;
|
|
118
|
+
twitterCards?: Record<string, string>;
|
|
119
|
+
canonicalUrl?: string;
|
|
120
|
+
detectedBrand?: string;
|
|
121
|
+
detectedLanguage?: string;
|
|
122
|
+
contentType?: string;
|
|
123
|
+
server?: string;
|
|
124
|
+
country?: string;
|
|
125
|
+
hostingProvider?: string;
|
|
126
|
+
asn?: string;
|
|
127
|
+
}
|
|
128
|
+
interface ExecutionStats {
|
|
129
|
+
totalTimeMs: number;
|
|
130
|
+
startTime: number;
|
|
131
|
+
endTime: number;
|
|
132
|
+
}
|
|
133
|
+
interface VerificationMeta {
|
|
134
|
+
mode: 'offline' | 'online' | 'hybrid';
|
|
135
|
+
engine: 'heuristics' | 'network' | 'cloud' | 'combined';
|
|
136
|
+
version: string;
|
|
137
|
+
duration: number;
|
|
138
|
+
cached: boolean;
|
|
139
|
+
cacheSource: 'memory' | 'redis' | 'filesystem' | 'cloud' | 'none';
|
|
140
|
+
timestamp: string;
|
|
141
|
+
}
|
|
142
|
+
interface Evidence {
|
|
143
|
+
id: string;
|
|
144
|
+
title: string;
|
|
145
|
+
category: string;
|
|
146
|
+
status: 'passed' | 'failed' | 'skipped' | 'unknown';
|
|
147
|
+
weight: number;
|
|
148
|
+
message: string;
|
|
149
|
+
recommendation?: string;
|
|
150
|
+
documentationUrl?: string;
|
|
151
|
+
}
|
|
152
|
+
interface Capabilities {
|
|
153
|
+
runtime: string;
|
|
154
|
+
performed: string[];
|
|
155
|
+
skipped: string[];
|
|
156
|
+
}
|
|
157
|
+
interface SecurityBadge {
|
|
158
|
+
label: string;
|
|
159
|
+
variant: 'success' | 'warn' | 'block' | 'unknown' | 'allow';
|
|
160
|
+
icon: string;
|
|
161
|
+
color: string;
|
|
162
|
+
}
|
|
163
|
+
interface VisualScore {
|
|
164
|
+
value: number;
|
|
165
|
+
max: number;
|
|
166
|
+
grade: 'A+' | 'A' | 'B' | 'C' | 'D' | 'F';
|
|
167
|
+
label: string;
|
|
168
|
+
}
|
|
169
|
+
interface ThreatDetails {
|
|
170
|
+
level: string;
|
|
171
|
+
category: string;
|
|
172
|
+
family: string | null;
|
|
173
|
+
techniques: string[];
|
|
174
|
+
}
|
|
175
|
+
interface UrlDetails {
|
|
176
|
+
original: string;
|
|
177
|
+
normalized: string;
|
|
178
|
+
hostname: string;
|
|
179
|
+
domain: string;
|
|
180
|
+
subdomain: string;
|
|
181
|
+
tld: string;
|
|
182
|
+
protocol: string;
|
|
183
|
+
port: string;
|
|
184
|
+
path: string;
|
|
185
|
+
query: string;
|
|
186
|
+
hash: string;
|
|
187
|
+
}
|
|
188
|
+
interface PerformanceMetrics {
|
|
189
|
+
duration: number;
|
|
190
|
+
cacheHit: boolean;
|
|
191
|
+
cacheKey?: string;
|
|
192
|
+
pluginsExecuted: number;
|
|
193
|
+
pluginsSkipped: number;
|
|
194
|
+
}
|
|
195
|
+
interface PluginExecutionDetails {
|
|
196
|
+
plugin: string;
|
|
197
|
+
status: 'passed' | 'failed' | 'skipped';
|
|
198
|
+
duration: number;
|
|
199
|
+
scoreContribution: number;
|
|
200
|
+
}
|
|
201
|
+
interface VerificationResult {
|
|
202
|
+
safe: boolean;
|
|
203
|
+
decision: DecisionAction;
|
|
204
|
+
classification: Classification;
|
|
205
|
+
trustScore: number;
|
|
206
|
+
riskScore: number;
|
|
207
|
+
confidence: number;
|
|
208
|
+
severity: RiskSeverity | string;
|
|
209
|
+
summary: string;
|
|
210
|
+
recommendation: string;
|
|
211
|
+
runtime: string;
|
|
212
|
+
verification: VerificationMeta;
|
|
213
|
+
evidence: Evidence[];
|
|
214
|
+
capabilities: Capabilities;
|
|
215
|
+
badge: SecurityBadge;
|
|
216
|
+
score: VisualScore;
|
|
217
|
+
threat: ThreatDetails;
|
|
218
|
+
url: UrlDetails;
|
|
219
|
+
performance: PerformanceMetrics;
|
|
220
|
+
pluginResults?: PluginExecutionDetails[];
|
|
221
|
+
riskLevel?: RiskLevel;
|
|
222
|
+
reasons?: string[];
|
|
223
|
+
recommendations?: string[];
|
|
224
|
+
checks?: CheckResult[];
|
|
225
|
+
categories?: Record<string, number>;
|
|
226
|
+
providerResults?: CheckResult[];
|
|
227
|
+
redirectChain?: string[];
|
|
228
|
+
redirectTrace?: RedirectTrace;
|
|
229
|
+
fromCache?: boolean;
|
|
230
|
+
action?: string;
|
|
231
|
+
policy?: string;
|
|
232
|
+
timeline?: ExecutionTimeline[];
|
|
233
|
+
execution?: ExecutionStats;
|
|
234
|
+
metadata?: RichMetadata | Record<string, unknown>;
|
|
235
|
+
isSafe?: () => boolean;
|
|
236
|
+
shouldWarn?: () => boolean;
|
|
237
|
+
shouldBlock?: () => boolean;
|
|
238
|
+
toJSON?: () => any;
|
|
239
|
+
toString?: () => string;
|
|
240
|
+
toMarkdown?: () => string;
|
|
241
|
+
toHTML?: () => string;
|
|
242
|
+
export?: (format: 'json' | 'markdown' | 'html') => string;
|
|
243
|
+
}
|
|
244
|
+
interface PickledResult {
|
|
245
|
+
url: string;
|
|
246
|
+
safe: boolean;
|
|
247
|
+
decision: DecisionAction;
|
|
248
|
+
trustScore: number;
|
|
249
|
+
riskScore: number;
|
|
250
|
+
classification: Classification;
|
|
251
|
+
threatLevel: ThreatLevel;
|
|
252
|
+
securityBadge: string;
|
|
253
|
+
riskColor: string;
|
|
254
|
+
summary: string;
|
|
255
|
+
recommendation: string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export type { Capabilities, CheckResult, Classification, DecisionAction, Evidence, ExecutionStats, ExecutionTimeline, HttpsStatus, ObservabilityHooks, PerformanceMetrics, PickledResult, PluginExecutionDetails, Provider, RedirectAnomalyKind, RedirectHop, RedirectTrace, RichMetadata, RiskCategory, RiskLevel, RiskSeverity, SecurityBadge, ThreatDetails, ThreatLevel, UrlDetails, VerificationMeta, VerificationResult, VerifyOptions, VisualScore };
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
export { MemoryCache, defaultCache } from '@safe-link-checker/cache/memory.js';
|
|
2
|
+
export { LRUCache } from '@safe-link-checker/cache/lru.js';
|
|
3
|
+
export { EventEmitter } from './core/events.js';
|
|
4
|
+
export { DefaultPluginFactory } from './core/factory.js';
|
|
5
|
+
export { PluginContext, PluginManager, PluginType, VerificationPlugin } from './core/plugin.js';
|
|
6
|
+
export { ConsensusEngine } from './engine/consensus.js';
|
|
7
|
+
export { PolicyEngine } from './engine/policy.js';
|
|
8
|
+
export { RuleEnginePlugin } from './engine/rules.js';
|
|
9
|
+
export { normalizeLink } from './utils/normalize.js';
|
|
10
|
+
export { validateUrl } from './validators/url.js';
|
|
11
|
+
export { validateIp } from './validators/ip.js';
|
|
12
|
+
export { validatePunycode } from './validators/punycode.js';
|
|
13
|
+
export { validateShortener } from './validators/shortener.js';
|
|
14
|
+
export { validateHeuristics } from './validators/heuristic.js';
|
|
15
|
+
export { AnalyticsTracker } from './analytics/tracker.js';
|
|
16
|
+
export { RealtimeSubscriptionEngine } from './realtime/subscription.js';
|
|
17
|
+
export { ReputationEngine, ReputationResult } from './engine/reputation.js';
|
|
18
|
+
export { CloudGateway } from './cloud/gateway.js';
|
|
19
|
+
export { formatResult } from './utils/formatter.js';
|
|
20
|
+
export { ReportData, createSecurityReport, injectReportHelpers } from './utils/report.js';
|
|
21
|
+
export { extractUrls, verifyLink, verifyLinks } from './verify.js';
|
|
22
|
+
export { CheckerOptions, SafeLinkChecker, SafeLinkError, TimeoutError } from './checker.js';
|
|
23
|
+
import './base.js';
|
|
24
|
+
import './validators/https.js';
|
|
25
|
+
import './validators/redirect.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* SafeLinkChecker
|
|
29
|
+
* Copyright (c) 2026
|
|
30
|
+
*
|
|
31
|
+
* This source code is licensed under the MIT license found in the
|
|
32
|
+
* LICENSE file in the root directory of this source tree.
|
|
33
|
+
*/
|
|
34
|
+
type RiskLevel = 'SAFE' | 'SUSPICIOUS' | 'DANGEROUS';
|
|
35
|
+
type HttpsStatus = 'HTTPS' | 'HTTP_ONLY' | 'CERT_ERROR' | 'TIMEOUT' | 'UNREACHABLE' | 'SKIPPED';
|
|
36
|
+
type RedirectAnomalyKind = 'LOOP' | 'PROTOCOL_DOWNGRADE' | 'MAX_REDIRECTS_EXCEEDED';
|
|
37
|
+
type RiskCategory = 'domain' | 'certificate' | 'redirect' | 'content' | 'network' | 'provider' | 'browser' | 'email' | 'qr' | 'download' | 'behavior' | 'ai' | 'other';
|
|
38
|
+
type RiskSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
39
|
+
type DecisionAction = 'allow' | 'warn' | 'block' | 'unknown' | 'ALLOW' | 'WARN' | 'REVIEW' | 'BLOCK' | 'ESCALATE';
|
|
40
|
+
type Classification = 'trusted' | 'safe' | 'suspicious' | 'unknown' | 'tracking' | 'spam' | 'phishing' | 'malware' | 'credential_theft' | 'scam' | 'brand_impersonation' | 'typosquatting' | 'Safe' | 'Suspicious' | 'Phishing' | 'Malware' | 'Scam' | 'Typosquatting' | 'Brand Impersonation' | 'Tracking' | 'Unsafe' | 'Unknown';
|
|
41
|
+
type ThreatLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' | 'UNKNOWN';
|
|
42
|
+
interface ObservabilityHooks {
|
|
43
|
+
onStart?: (url: string, options: VerifyOptions) => void;
|
|
44
|
+
onFinish?: (result: VerificationResult) => void;
|
|
45
|
+
onWarning?: (url: string, reasons: string[]) => void;
|
|
46
|
+
onBlocked?: (url: string, reasons: string[]) => void;
|
|
47
|
+
onProvider?: (providerName: string, result: CheckResult) => void;
|
|
48
|
+
onCacheHit?: (url: string) => void;
|
|
49
|
+
onCacheMiss?: (url: string) => void;
|
|
50
|
+
}
|
|
51
|
+
interface VerifyOptions {
|
|
52
|
+
maxRedirects?: number;
|
|
53
|
+
timeout?: number;
|
|
54
|
+
customShorteners?: string[];
|
|
55
|
+
bypassCache?: boolean;
|
|
56
|
+
removeTrackingParams?: boolean;
|
|
57
|
+
checkHttps?: boolean;
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
policy?: string;
|
|
60
|
+
hooks?: ObservabilityHooks;
|
|
61
|
+
realtime?: boolean;
|
|
62
|
+
cloud?: {
|
|
63
|
+
enabled: boolean;
|
|
64
|
+
apiKey: string;
|
|
65
|
+
endpoint?: string;
|
|
66
|
+
};
|
|
67
|
+
telemetry?: {
|
|
68
|
+
enabled: boolean;
|
|
69
|
+
level?: 'anonymous' | 'full';
|
|
70
|
+
};
|
|
71
|
+
debug?: boolean;
|
|
72
|
+
}
|
|
73
|
+
interface CheckResult {
|
|
74
|
+
name: string;
|
|
75
|
+
safe: boolean;
|
|
76
|
+
scoreImpact: number;
|
|
77
|
+
message: string;
|
|
78
|
+
weight?: number;
|
|
79
|
+
fatal?: boolean;
|
|
80
|
+
detector?: string;
|
|
81
|
+
category?: RiskCategory;
|
|
82
|
+
severity?: RiskSeverity;
|
|
83
|
+
confidence?: number;
|
|
84
|
+
scoreContribution?: number;
|
|
85
|
+
title?: string;
|
|
86
|
+
description?: string;
|
|
87
|
+
recommendation?: string;
|
|
88
|
+
references?: string[];
|
|
89
|
+
executionTime?: number;
|
|
90
|
+
timestamp?: number;
|
|
91
|
+
metadata?: Record<string, unknown>;
|
|
92
|
+
}
|
|
93
|
+
interface Provider {
|
|
94
|
+
name: string;
|
|
95
|
+
check(url: string, options?: VerifyOptions): Promise<CheckResult | null>;
|
|
96
|
+
}
|
|
97
|
+
interface RedirectHop {
|
|
98
|
+
url: string;
|
|
99
|
+
statusCode: number;
|
|
100
|
+
}
|
|
101
|
+
interface RedirectTrace {
|
|
102
|
+
chain: string[];
|
|
103
|
+
finalUrl: string;
|
|
104
|
+
redirectCount: number;
|
|
105
|
+
anomalies: RedirectAnomalyKind[];
|
|
106
|
+
}
|
|
107
|
+
interface ExecutionTimeline {
|
|
108
|
+
phase: string;
|
|
109
|
+
startTime: number;
|
|
110
|
+
durationMs: number;
|
|
111
|
+
status: 'success' | 'error' | 'skipped';
|
|
112
|
+
}
|
|
113
|
+
interface RichMetadata {
|
|
114
|
+
favicon?: string;
|
|
115
|
+
title?: string;
|
|
116
|
+
description?: string;
|
|
117
|
+
openGraph?: Record<string, string>;
|
|
118
|
+
twitterCards?: Record<string, string>;
|
|
119
|
+
canonicalUrl?: string;
|
|
120
|
+
detectedBrand?: string;
|
|
121
|
+
detectedLanguage?: string;
|
|
122
|
+
contentType?: string;
|
|
123
|
+
server?: string;
|
|
124
|
+
country?: string;
|
|
125
|
+
hostingProvider?: string;
|
|
126
|
+
asn?: string;
|
|
127
|
+
}
|
|
128
|
+
interface ExecutionStats {
|
|
129
|
+
totalTimeMs: number;
|
|
130
|
+
startTime: number;
|
|
131
|
+
endTime: number;
|
|
132
|
+
}
|
|
133
|
+
interface VerificationMeta {
|
|
134
|
+
mode: 'offline' | 'online' | 'hybrid';
|
|
135
|
+
engine: 'heuristics' | 'network' | 'cloud' | 'combined';
|
|
136
|
+
version: string;
|
|
137
|
+
duration: number;
|
|
138
|
+
cached: boolean;
|
|
139
|
+
cacheSource: 'memory' | 'redis' | 'filesystem' | 'cloud' | 'none';
|
|
140
|
+
timestamp: string;
|
|
141
|
+
}
|
|
142
|
+
interface Evidence {
|
|
143
|
+
id: string;
|
|
144
|
+
title: string;
|
|
145
|
+
category: string;
|
|
146
|
+
status: 'passed' | 'failed' | 'skipped' | 'unknown';
|
|
147
|
+
weight: number;
|
|
148
|
+
message: string;
|
|
149
|
+
recommendation?: string;
|
|
150
|
+
documentationUrl?: string;
|
|
151
|
+
}
|
|
152
|
+
interface Capabilities {
|
|
153
|
+
runtime: string;
|
|
154
|
+
performed: string[];
|
|
155
|
+
skipped: string[];
|
|
156
|
+
}
|
|
157
|
+
interface SecurityBadge {
|
|
158
|
+
label: string;
|
|
159
|
+
variant: 'success' | 'warn' | 'block' | 'unknown' | 'allow';
|
|
160
|
+
icon: string;
|
|
161
|
+
color: string;
|
|
162
|
+
}
|
|
163
|
+
interface VisualScore {
|
|
164
|
+
value: number;
|
|
165
|
+
max: number;
|
|
166
|
+
grade: 'A+' | 'A' | 'B' | 'C' | 'D' | 'F';
|
|
167
|
+
label: string;
|
|
168
|
+
}
|
|
169
|
+
interface ThreatDetails {
|
|
170
|
+
level: string;
|
|
171
|
+
category: string;
|
|
172
|
+
family: string | null;
|
|
173
|
+
techniques: string[];
|
|
174
|
+
}
|
|
175
|
+
interface UrlDetails {
|
|
176
|
+
original: string;
|
|
177
|
+
normalized: string;
|
|
178
|
+
hostname: string;
|
|
179
|
+
domain: string;
|
|
180
|
+
subdomain: string;
|
|
181
|
+
tld: string;
|
|
182
|
+
protocol: string;
|
|
183
|
+
port: string;
|
|
184
|
+
path: string;
|
|
185
|
+
query: string;
|
|
186
|
+
hash: string;
|
|
187
|
+
}
|
|
188
|
+
interface PerformanceMetrics {
|
|
189
|
+
duration: number;
|
|
190
|
+
cacheHit: boolean;
|
|
191
|
+
cacheKey?: string;
|
|
192
|
+
pluginsExecuted: number;
|
|
193
|
+
pluginsSkipped: number;
|
|
194
|
+
}
|
|
195
|
+
interface PluginExecutionDetails {
|
|
196
|
+
plugin: string;
|
|
197
|
+
status: 'passed' | 'failed' | 'skipped';
|
|
198
|
+
duration: number;
|
|
199
|
+
scoreContribution: number;
|
|
200
|
+
}
|
|
201
|
+
interface VerificationResult {
|
|
202
|
+
safe: boolean;
|
|
203
|
+
decision: DecisionAction;
|
|
204
|
+
classification: Classification;
|
|
205
|
+
trustScore: number;
|
|
206
|
+
riskScore: number;
|
|
207
|
+
confidence: number;
|
|
208
|
+
severity: RiskSeverity | string;
|
|
209
|
+
summary: string;
|
|
210
|
+
recommendation: string;
|
|
211
|
+
runtime: string;
|
|
212
|
+
verification: VerificationMeta;
|
|
213
|
+
evidence: Evidence[];
|
|
214
|
+
capabilities: Capabilities;
|
|
215
|
+
badge: SecurityBadge;
|
|
216
|
+
score: VisualScore;
|
|
217
|
+
threat: ThreatDetails;
|
|
218
|
+
url: UrlDetails;
|
|
219
|
+
performance: PerformanceMetrics;
|
|
220
|
+
pluginResults?: PluginExecutionDetails[];
|
|
221
|
+
riskLevel?: RiskLevel;
|
|
222
|
+
reasons?: string[];
|
|
223
|
+
recommendations?: string[];
|
|
224
|
+
checks?: CheckResult[];
|
|
225
|
+
categories?: Record<string, number>;
|
|
226
|
+
providerResults?: CheckResult[];
|
|
227
|
+
redirectChain?: string[];
|
|
228
|
+
redirectTrace?: RedirectTrace;
|
|
229
|
+
fromCache?: boolean;
|
|
230
|
+
action?: string;
|
|
231
|
+
policy?: string;
|
|
232
|
+
timeline?: ExecutionTimeline[];
|
|
233
|
+
execution?: ExecutionStats;
|
|
234
|
+
metadata?: RichMetadata | Record<string, unknown>;
|
|
235
|
+
isSafe?: () => boolean;
|
|
236
|
+
shouldWarn?: () => boolean;
|
|
237
|
+
shouldBlock?: () => boolean;
|
|
238
|
+
toJSON?: () => any;
|
|
239
|
+
toString?: () => string;
|
|
240
|
+
toMarkdown?: () => string;
|
|
241
|
+
toHTML?: () => string;
|
|
242
|
+
export?: (format: 'json' | 'markdown' | 'html') => string;
|
|
243
|
+
}
|
|
244
|
+
interface PickledResult {
|
|
245
|
+
url: string;
|
|
246
|
+
safe: boolean;
|
|
247
|
+
decision: DecisionAction;
|
|
248
|
+
trustScore: number;
|
|
249
|
+
riskScore: number;
|
|
250
|
+
classification: Classification;
|
|
251
|
+
threatLevel: ThreatLevel;
|
|
252
|
+
securityBadge: string;
|
|
253
|
+
riskColor: string;
|
|
254
|
+
summary: string;
|
|
255
|
+
recommendation: string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export type { Capabilities, CheckResult, Classification, DecisionAction, Evidence, ExecutionStats, ExecutionTimeline, HttpsStatus, ObservabilityHooks, PerformanceMetrics, PickledResult, PluginExecutionDetails, Provider, RedirectAnomalyKind, RedirectHop, RedirectTrace, RichMetadata, RiskCategory, RiskLevel, RiskSeverity, SecurityBadge, ThreatDetails, ThreatLevel, UrlDetails, VerificationMeta, VerificationResult, VerifyOptions, VisualScore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{c as SafeLinkChecker,a as SafeLinkError,b as TimeoutError,f as extractUrls,d as verifyLink,e as verifyLinks}from'../chunk-DDF3IZSI.js';export{o as AnalyticsTracker,r as CloudGateway,f as ConsensusEngine,d as DefaultPluginFactory,c as EventEmitter,a as LRUCache,a as MemoryCache,e as PluginManager,g as PolicyEngine,p as RealtimeSubscriptionEngine,q as ReputationEngine,h as RuleEnginePlugin,t as createSecurityReport,b as defaultCache,s as formatResult,u as injectReportHelpers,i as normalizeLink,n as validateHeuristics,k as validateIp,l as validatePunycode,m as validateShortener,j as validateUrl}from'../chunk-LWMILG2I.js';
|