contrastapi 1.0.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 +126 -0
- package/index.d.ts +62 -0
- package/index.js +196 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# ContrastAPI Node.js SDK
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for [ContrastAPI](https://api.contrastcyber.com) — security intelligence for developers and AI agents.
|
|
4
|
+
|
|
5
|
+
Zero dependencies. Works with Node.js 14+.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install contrastapi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const ContrastAPI = require("contrastapi");
|
|
17
|
+
const api = ContrastAPI();
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
// Domain intelligence
|
|
21
|
+
const report = await api.domain.report("example.com");
|
|
22
|
+
|
|
23
|
+
// CVE lookup
|
|
24
|
+
const cve = await api.cve.lookup("CVE-2024-3094");
|
|
25
|
+
|
|
26
|
+
// SSL certificate check
|
|
27
|
+
const ssl = await api.domain.ssl("example.com");
|
|
28
|
+
|
|
29
|
+
// Scan HTTP security headers (live)
|
|
30
|
+
const headers = await api.scan.headers("example.com");
|
|
31
|
+
|
|
32
|
+
// Check code for secrets
|
|
33
|
+
const secrets = await api.check.secrets("const key = 'AKIA...'", "javascript");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## With API Key (Pro)
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
const api = ContrastAPI({ apiKey: "your-api-key" });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## All Methods
|
|
46
|
+
|
|
47
|
+
### Domain Intelligence
|
|
48
|
+
```javascript
|
|
49
|
+
api.domain.report("example.com") // Full domain report
|
|
50
|
+
api.domain.report("example.com", {lite: true}) // Fast lite report
|
|
51
|
+
api.domain.dns("example.com") // DNS records
|
|
52
|
+
api.domain.whois("example.com") // WHOIS data
|
|
53
|
+
api.domain.subdomains("example.com") // Subdomain enumeration
|
|
54
|
+
api.domain.certs("example.com") // Certificate transparency
|
|
55
|
+
api.domain.ssl("example.com") // SSL/TLS analysis
|
|
56
|
+
api.domain.tech("example.com") // Technology fingerprint
|
|
57
|
+
api.domain.threat("example.com") // Threat intelligence
|
|
58
|
+
api.domain.monitor("example.com") // Domain monitoring
|
|
59
|
+
api.domain.vulns("example.com") // Known vulnerabilities
|
|
60
|
+
api.domain.bulk(["a.com", "b.com"]) // Bulk domain reports
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### IP & ASN
|
|
64
|
+
```javascript
|
|
65
|
+
api.ip.lookup("8.8.8.8") // IP intelligence
|
|
66
|
+
api.asn.lookup("google.com") // ASN lookup
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### CVE Intelligence
|
|
70
|
+
```javascript
|
|
71
|
+
api.cve.lookup("CVE-2024-3094") // Single CVE
|
|
72
|
+
api.cve.search({product: "apache", severity: "critical"})
|
|
73
|
+
api.cve.recent() // Recently published
|
|
74
|
+
api.cve.kev() // Known exploited (CISA KEV)
|
|
75
|
+
api.cve.epss("CVE-2024-3094") // EPSS score
|
|
76
|
+
api.cve.exploit("CVE-2024-3094") // Public exploits
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Threat Intelligence
|
|
80
|
+
```javascript
|
|
81
|
+
api.ioc.lookup("evil.com") // IOC enrichment (auto-detect type)
|
|
82
|
+
api.ioc.hash("abc123...") // Malware hash lookup
|
|
83
|
+
api.ioc.phishing("https://evil.com/login") // Phishing check
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Email & Phone
|
|
87
|
+
```javascript
|
|
88
|
+
api.email.mx("example.com") // MX + SPF/DMARC/DKIM
|
|
89
|
+
api.email.disposable("user@tempmail.com") // Disposable email check
|
|
90
|
+
api.phone.lookup("+1234567890") // Phone validation
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Password
|
|
94
|
+
```javascript
|
|
95
|
+
api.password.check("5baa61e4...") // HIBP breach check (SHA1)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Code Security
|
|
99
|
+
```javascript
|
|
100
|
+
api.check.secrets(code, "python") // Detect hardcoded secrets
|
|
101
|
+
api.check.injection(code, "javascript") // SQL/command injection
|
|
102
|
+
api.check.headers({"Content-Security-Policy": "..."}) // Header validation
|
|
103
|
+
api.check.dependencies([{name: "lodash", version: "4.17.0"}]) // CVE check
|
|
104
|
+
api.scan.headers("example.com") // Live header scan
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Meta
|
|
108
|
+
```javascript
|
|
109
|
+
api.status() // API health
|
|
110
|
+
api.usage() // Usage stats (Pro)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Error Handling
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
try {
|
|
117
|
+
const result = await api.cve.lookup("CVE-9999-0000");
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.log(err.status); // 404
|
|
120
|
+
console.log(err.message); // "CVE not found"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
declare function ContrastAPI(options?: {
|
|
2
|
+
baseUrl?: string;
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
allowInsecure?: boolean;
|
|
6
|
+
}): {
|
|
7
|
+
domain: {
|
|
8
|
+
report(domain: string, opts?: { lite?: boolean }): Promise<any>;
|
|
9
|
+
dns(domain: string): Promise<any>;
|
|
10
|
+
whois(domain: string): Promise<any>;
|
|
11
|
+
subdomains(domain: string): Promise<any>;
|
|
12
|
+
certs(domain: string): Promise<any>;
|
|
13
|
+
ssl(domain: string): Promise<any>;
|
|
14
|
+
tech(domain: string): Promise<any>;
|
|
15
|
+
threat(domain: string): Promise<any>;
|
|
16
|
+
monitor(domain: string): Promise<any>;
|
|
17
|
+
vulns(domain: string): Promise<any>;
|
|
18
|
+
bulk(domains: string[]): Promise<any>;
|
|
19
|
+
};
|
|
20
|
+
ip: {
|
|
21
|
+
lookup(ip: string): Promise<any>;
|
|
22
|
+
};
|
|
23
|
+
asn: {
|
|
24
|
+
lookup(target: string): Promise<any>;
|
|
25
|
+
};
|
|
26
|
+
cve: {
|
|
27
|
+
lookup(cveId: string): Promise<any>;
|
|
28
|
+
search(params?: { product?: string; severity?: string; days?: number; limit?: number }): Promise<any>;
|
|
29
|
+
recent(params?: { hours?: number; limit?: number }): Promise<any>;
|
|
30
|
+
kev(params?: { limit?: number }): Promise<any>;
|
|
31
|
+
epss(cveId: string): Promise<any>;
|
|
32
|
+
exploit(cveId: string): Promise<any>;
|
|
33
|
+
};
|
|
34
|
+
ioc: {
|
|
35
|
+
lookup(indicator: string): Promise<any>;
|
|
36
|
+
hash(fileHash: string): Promise<any>;
|
|
37
|
+
phishing(url: string): Promise<any>;
|
|
38
|
+
};
|
|
39
|
+
email: {
|
|
40
|
+
mx(domain: string): Promise<any>;
|
|
41
|
+
disposable(email: string): Promise<any>;
|
|
42
|
+
};
|
|
43
|
+
phone: {
|
|
44
|
+
lookup(number: string): Promise<any>;
|
|
45
|
+
};
|
|
46
|
+
password: {
|
|
47
|
+
check(sha1Hash: string): Promise<any>;
|
|
48
|
+
};
|
|
49
|
+
check: {
|
|
50
|
+
secrets(code: string, language: string): Promise<any>;
|
|
51
|
+
injection(code: string, language: string): Promise<any>;
|
|
52
|
+
headers(headers: Record<string, string>): Promise<any>;
|
|
53
|
+
dependencies(packages: Array<{ name: string; version?: string }>): Promise<any>;
|
|
54
|
+
};
|
|
55
|
+
scan: {
|
|
56
|
+
headers(domain: string): Promise<any>;
|
|
57
|
+
};
|
|
58
|
+
status(): Promise<any>;
|
|
59
|
+
usage(): Promise<any>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export = ContrastAPI;
|
package/index.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContrastAPI — Security Intelligence SDK
|
|
3
|
+
* https://api.contrastcyber.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const https = require("https");
|
|
7
|
+
const http = require("http");
|
|
8
|
+
|
|
9
|
+
const DEFAULT_BASE = "https://api.contrastcyber.com";
|
|
10
|
+
const MAX_BODY = 10 * 1024 * 1024; // 10 MB
|
|
11
|
+
const VERSION = require("./package.json").version;
|
|
12
|
+
|
|
13
|
+
function enc(v) {
|
|
14
|
+
if (!v && v !== 0) throw new Error("Missing required parameter");
|
|
15
|
+
return encodeURIComponent(String(v));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function encPath(v) {
|
|
19
|
+
if (!v) throw new Error("Missing required parameter");
|
|
20
|
+
return String(v).split("/").map(encodeURIComponent).join("/");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ContrastAPI(options = {}) {
|
|
24
|
+
const baseUrl = (options.baseUrl || DEFAULT_BASE).replace(/\/$/, "");
|
|
25
|
+
const apiKey = options.apiKey || null;
|
|
26
|
+
const timeout = Math.max(1000, Math.min(options.timeout || 30000, 120000));
|
|
27
|
+
|
|
28
|
+
if (!baseUrl.startsWith("https://") && !options.allowInsecure) {
|
|
29
|
+
throw new Error("Only HTTPS is allowed. Pass { allowInsecure: true } to override.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function request(method, path, body) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const url = new URL(path, baseUrl);
|
|
35
|
+
if (url.origin !== new URL(baseUrl).origin) {
|
|
36
|
+
return reject(new Error("URL origin mismatch"));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const headers = {
|
|
40
|
+
"Accept": "application/json",
|
|
41
|
+
"User-Agent": `contrastapi-node/${VERSION}`,
|
|
42
|
+
};
|
|
43
|
+
if (apiKey) {
|
|
44
|
+
if (url.protocol !== "https:") {
|
|
45
|
+
return reject(new Error("Refusing to send API key over insecure connection"));
|
|
46
|
+
}
|
|
47
|
+
headers["X-API-Key"] = apiKey;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let payload;
|
|
51
|
+
if (body) {
|
|
52
|
+
payload = JSON.stringify(body);
|
|
53
|
+
headers["Content-Type"] = "application/json";
|
|
54
|
+
headers["Content-Length"] = Buffer.byteLength(payload);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const mod = url.protocol === "https:" ? https : http;
|
|
58
|
+
const req = mod.request(url, { method, headers, timeout }, (res) => {
|
|
59
|
+
let data = "";
|
|
60
|
+
let size = 0;
|
|
61
|
+
res.on("data", (chunk) => {
|
|
62
|
+
size += chunk.length;
|
|
63
|
+
if (size > MAX_BODY) {
|
|
64
|
+
req.destroy();
|
|
65
|
+
return reject(new Error("Response too large"));
|
|
66
|
+
}
|
|
67
|
+
data += chunk;
|
|
68
|
+
});
|
|
69
|
+
res.on("end", () => {
|
|
70
|
+
try {
|
|
71
|
+
const json = JSON.parse(data);
|
|
72
|
+
if (res.statusCode >= 400) {
|
|
73
|
+
const err = new Error(json.detail || json.message || `HTTP ${res.statusCode}`);
|
|
74
|
+
err.status = res.statusCode;
|
|
75
|
+
reject(err);
|
|
76
|
+
} else {
|
|
77
|
+
resolve(json);
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
reject(new Error(`Invalid JSON response (HTTP ${res.statusCode})`));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
req.on("error", reject);
|
|
86
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("Request timed out")); });
|
|
87
|
+
if (payload) req.write(payload);
|
|
88
|
+
req.end();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function get(path) { return request("GET", path); }
|
|
93
|
+
function post(path, body) { return request("POST", path, body); }
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
// --- Domain Intelligence ---
|
|
97
|
+
domain: {
|
|
98
|
+
report: (domain, opts = {}) => get(`/v1/domain/${enc(domain)}${opts.lite ? "?lite=true" : ""}`),
|
|
99
|
+
dns: (domain) => get(`/v1/dns/${enc(domain)}`),
|
|
100
|
+
whois: (domain) => get(`/v1/whois/${enc(domain)}`),
|
|
101
|
+
subdomains: (domain) => get(`/v1/subdomains/${enc(domain)}`),
|
|
102
|
+
certs: (domain) => get(`/v1/certs/${enc(domain)}`),
|
|
103
|
+
ssl: (domain) => get(`/v1/ssl/${enc(domain)}`),
|
|
104
|
+
tech: (domain) => get(`/v1/tech/${enc(domain)}`),
|
|
105
|
+
threat: (domain) => get(`/v1/threat/${enc(domain)}`),
|
|
106
|
+
monitor: (domain) => get(`/v1/monitor/${enc(domain)}`),
|
|
107
|
+
vulns: (domain) => get(`/v1/domain/${enc(domain)}/vulns`),
|
|
108
|
+
bulk: (domains) => {
|
|
109
|
+
if (!Array.isArray(domains) || !domains.every(d => typeof d === "string")) {
|
|
110
|
+
throw new Error("domains must be an array of strings");
|
|
111
|
+
}
|
|
112
|
+
return post("/v1/domains/bulk", { domains });
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
// --- IP Intelligence ---
|
|
117
|
+
ip: {
|
|
118
|
+
lookup: (ip) => get(`/v1/ip/${enc(ip)}`),
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// --- ASN ---
|
|
122
|
+
asn: {
|
|
123
|
+
lookup: (target) => get(`/v1/asn/${enc(target)}`),
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// --- CVE Intelligence ---
|
|
127
|
+
cve: {
|
|
128
|
+
lookup: (cveId) => get(`/v1/cve/${enc(cveId)}`),
|
|
129
|
+
search: (params = {}) => {
|
|
130
|
+
const q = new URLSearchParams();
|
|
131
|
+
if (params.product) q.set("product", params.product);
|
|
132
|
+
if (params.severity) q.set("severity", params.severity);
|
|
133
|
+
if (params.days) q.set("days", params.days);
|
|
134
|
+
if (params.limit) q.set("limit", params.limit);
|
|
135
|
+
return get(`/v1/cves?${q}`);
|
|
136
|
+
},
|
|
137
|
+
recent: (params = {}) => {
|
|
138
|
+
const q = new URLSearchParams();
|
|
139
|
+
if (params.hours) q.set("hours", params.hours);
|
|
140
|
+
if (params.limit) q.set("limit", params.limit);
|
|
141
|
+
const qs = q.toString();
|
|
142
|
+
return get(`/v1/cves/recent${qs ? "?" + qs : ""}`);
|
|
143
|
+
},
|
|
144
|
+
kev: (params = {}) => {
|
|
145
|
+
const q = new URLSearchParams();
|
|
146
|
+
if (params.limit) q.set("limit", params.limit);
|
|
147
|
+
const qs = q.toString();
|
|
148
|
+
return get(`/v1/cves/kev${qs ? "?" + qs : ""}`);
|
|
149
|
+
},
|
|
150
|
+
epss: (cveId) => get(`/v1/epss/${enc(cveId)}`),
|
|
151
|
+
exploit: (cveId) => get(`/v1/exploit/${enc(cveId)}`),
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// --- Threat Intelligence / IOC ---
|
|
155
|
+
ioc: {
|
|
156
|
+
lookup: (indicator) => get(`/v1/ioc/${encPath(indicator)}`),
|
|
157
|
+
hash: (fileHash) => get(`/v1/hash/${enc(fileHash)}`),
|
|
158
|
+
phishing: (url) => get(`/v1/phishing/${encPath(url)}`),
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
// --- Email ---
|
|
162
|
+
email: {
|
|
163
|
+
mx: (domain) => get(`/v1/email/mx/${enc(domain)}`),
|
|
164
|
+
disposable: (email) => get(`/v1/email/disposable/${enc(email)}`),
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
// --- Phone ---
|
|
168
|
+
phone: {
|
|
169
|
+
lookup: (number) => get(`/v1/phone/${enc(number)}`),
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// --- Password ---
|
|
173
|
+
password: {
|
|
174
|
+
check: (sha1Hash) => get(`/v1/password/${enc(sha1Hash)}`),
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// --- Code Security ---
|
|
178
|
+
check: {
|
|
179
|
+
secrets: (code, language) => post("/v1/check/secrets", { code, language }),
|
|
180
|
+
injection: (code, language) => post("/v1/check/injection", { code, language }),
|
|
181
|
+
headers: (headers) => post("/v1/check/headers", { headers }),
|
|
182
|
+
dependencies: (packages) => post("/v1/check/dependencies", { packages }),
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
// --- Headers (live scan) ---
|
|
186
|
+
scan: {
|
|
187
|
+
headers: (domain) => get(`/v1/scan/headers/${enc(domain)}`),
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// --- Meta ---
|
|
191
|
+
status: () => get("/v1/status"),
|
|
192
|
+
usage: () => get("/v1/usage"),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = ContrastAPI;
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "contrastapi",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Node.js SDK for ContrastAPI — security intelligence for developers and AI agents",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "ContrastCyber <hello@contrastcyber.com>",
|
|
9
|
+
"homepage": "https://contrastcyber.com",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/UPinar/contrastapi"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"security",
|
|
16
|
+
"cve",
|
|
17
|
+
"vulnerability",
|
|
18
|
+
"domain",
|
|
19
|
+
"dns",
|
|
20
|
+
"whois",
|
|
21
|
+
"ssl",
|
|
22
|
+
"threat-intelligence",
|
|
23
|
+
"mcp",
|
|
24
|
+
"osint",
|
|
25
|
+
"api"
|
|
26
|
+
],
|
|
27
|
+
"files": [
|
|
28
|
+
"index.js",
|
|
29
|
+
"index.d.ts",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=14"
|
|
34
|
+
}
|
|
35
|
+
}
|