@socketsecurity/sdk 1.11.2 → 2.0.2
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 +42 -0
- package/README.md +53 -81
- package/dist/constants.d.ts +9 -1
- package/dist/http-client.d.ts +3 -1
- package/dist/socket-sdk-class.d.ts +25 -0
- package/dist/types.d.ts +10 -4
- package/package.json +48 -85
- package/types/api-helpers.d.ts +61 -53
- package/types/api.d.ts +1584 -926
- package/dist/constants.js +0 -30
- package/dist/file-upload.js +0 -142
- package/dist/http-client.js +0 -405
- package/dist/index.js +0 -47
- package/dist/package.json.js +0 -207
- package/dist/quota-utils.js +0 -175
- package/dist/socket-sdk-class.js +0 -1511
- package/dist/testing.js +0 -387
- package/dist/user-agent.js +0 -21
- package/dist/utils.js +0 -101
- /package/{requirements.json → data/api-method-quota-and-permissions.json} +0 -0
package/dist/constants.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var _package = require('./package.json.js');
|
|
4
|
-
var userAgent = require('./user-agent.js');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @fileoverview Configuration constants and enums for the Socket SDK.
|
|
8
|
-
* Provides default values, HTTP agents, and public policy configurations for API interactions.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const DEFAULT_USER_AGENT = userAgent.createUserAgentFromPkgJson(_package.default);
|
|
12
|
-
|
|
13
|
-
// https://github.com/sindresorhus/got/blob/v14.4.6/documentation/2-options.md#agent
|
|
14
|
-
// Valid HTTP agent names for Got-style agent configuration compatibility.
|
|
15
|
-
const httpAgentNames = new Set(['http', 'https', 'http2']);
|
|
16
|
-
|
|
17
|
-
// Public security policy.
|
|
18
|
-
const publicPolicy = new Map([
|
|
19
|
-
// error (1):
|
|
20
|
-
['malware', 'error'],
|
|
21
|
-
// warn (7):
|
|
22
|
-
['criticalCVE', 'warn'], ['didYouMean', 'warn'], ['gitDependency', 'warn'], ['httpDependency', 'warn'], ['licenseSpdxDisj', 'warn'], ['obfuscatedFile', 'warn'], ['troll', 'warn'],
|
|
23
|
-
// monitor (7):
|
|
24
|
-
['deprecated', 'monitor'], ['mediumCVE', 'monitor'], ['mildCVE', 'monitor'], ['shrinkwrap', 'monitor'], ['telemetry', 'monitor'], ['unpopularPackage', 'monitor'], ['unstableOwnership', 'monitor'],
|
|
25
|
-
// ignore (85):
|
|
26
|
-
['ambiguousClassifier', 'ignore'], ['badEncoding', 'ignore'], ['badSemver', 'ignore'], ['badSemverDependency', 'ignore'], ['bidi', 'ignore'], ['binScriptConfusion', 'ignore'], ['chromeContentScript', 'ignore'], ['chromeHostPermission', 'ignore'], ['chromePermission', 'ignore'], ['chromeWildcardHostPermission', 'ignore'], ['chronoAnomaly', 'ignore'], ['compromisedSSHKey', 'ignore'], ['copyleftLicense', 'ignore'], ['cve', 'ignore'], ['debugAccess', 'ignore'], ['deprecatedLicense', 'ignore'], ['deprecatedException', 'ignore'], ['dynamicRequire', 'ignore'], ['emptyPackage', 'ignore'], ['envVars', 'ignore'], ['explicitlyUnlicensedItem', 'ignore'], ['extraneousDependency', 'ignore'], ['fileDependency', 'ignore'], ['filesystemAccess', 'ignore'], ['floatingDependency', 'ignore'], ['gitHubDependency', 'ignore'], ['gptAnomaly', 'ignore'], ['gptDidYouMean', 'ignore'], ['gptMalware', 'ignore'], ['gptSecurity', 'ignore'], ['hasNativeCode', 'ignore'], ['highEntropyStrings', 'ignore'], ['homoglyphs', 'ignore'], ['installScripts', 'ignore'], ['invalidPackageJSON', 'ignore'], ['invisibleChars', 'ignore'], ['licenseChange', 'ignore'], ['licenseException', 'ignore'], ['longStrings', 'ignore'], ['majorRefactor', 'ignore'], ['manifestConfusion', 'ignore'], ['minifiedFile', 'ignore'], ['miscLicenseIssues', 'ignore'], ['missingAuthor', 'ignore'], ['missingDependency', 'ignore'], ['missingLicense', 'ignore'], ['missingTarball', 'ignore'], ['mixedLicense', 'ignore'], ['modifiedException', 'ignore'], ['modifiedLicense', 'ignore'], ['networkAccess', 'ignore'], ['newAuthor', 'ignore'], ['noAuthorData', 'ignore'], ['noBugTracker', 'ignore'], ['noLicenseFound', 'ignore'], ['noREADME', 'ignore'], ['noRepository', 'ignore'], ['noTests', 'ignore'], ['noV1', 'ignore'], ['noWebsite', 'ignore'], ['nonOSILicense', 'ignore'], ['nonSPDXLicense', 'ignore'], ['nonpermissiveLicense', 'ignore'], ['notice', 'ignore'], ['obfuscatedRequire', 'ignore'], ['peerDependency', 'ignore'], ['potentialVulnerability', 'ignore'], ['semverAnomaly', 'ignore'], ['shellAccess', 'ignore'], ['shellScriptOverride', 'ignore'], ['socketUpgradeAvailable', 'ignore'], ['suspiciousStarActivity', 'ignore'], ['suspiciousString', 'ignore'], ['trivialPackage', 'ignore'], ['typeModuleCompatibility', 'ignore'], ['uncaughtOptionalDependency', 'ignore'], ['unclearLicense', 'ignore'], ['unidentifiedLicense', 'ignore'], ['unmaintained', 'ignore'], ['unpublished', 'ignore'], ['unresolvedRequire', 'ignore'], ['unsafeCopyright', 'ignore'], ['unusedDependency', 'ignore'], ['urlStrings', 'ignore'], ['usesEval', 'ignore'], ['zeroWidth', 'ignore']]);
|
|
27
|
-
|
|
28
|
-
exports.DEFAULT_USER_AGENT = DEFAULT_USER_AGENT;
|
|
29
|
-
exports.httpAgentNames = httpAgentNames;
|
|
30
|
-
exports.publicPolicy = publicPolicy;
|
package/dist/file-upload.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var events = require('node:events');
|
|
4
|
-
var node_fs = require('node:fs');
|
|
5
|
-
var path$1 = require('node:path');
|
|
6
|
-
var node_stream = require('node:stream');
|
|
7
|
-
var path = require('@socketsecurity/registry/lib/path');
|
|
8
|
-
var httpClient = require('./http-client.js');
|
|
9
|
-
|
|
10
|
-
/** @fileoverview File upload utilities for Socket API with multipart form data support. */
|
|
11
|
-
/**
|
|
12
|
-
* Create multipart form-data body parts for file uploads.
|
|
13
|
-
* Converts file paths to readable streams with proper multipart headers.
|
|
14
|
-
*/
|
|
15
|
-
function createRequestBodyForFilepaths(filepaths, basePath) {
|
|
16
|
-
const requestBody = [];
|
|
17
|
-
for (const absPath of filepaths) {
|
|
18
|
-
const relPath = path.normalizePath(path$1.relative(basePath, absPath));
|
|
19
|
-
const filename = path$1.basename(absPath);
|
|
20
|
-
requestBody.push([`Content-Disposition: form-data; name="${relPath}"; filename="${filename}"\r\n`, 'Content-Type: application/octet-stream\r\n\r\n', node_fs.createReadStream(absPath, {
|
|
21
|
-
highWaterMark: 1024 * 1024
|
|
22
|
-
})]);
|
|
23
|
-
}
|
|
24
|
-
return requestBody;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Create multipart form-data body part for JSON data.
|
|
29
|
-
* Converts JSON object to readable stream with appropriate headers.
|
|
30
|
-
*/
|
|
31
|
-
function createRequestBodyForJson(jsonData, basename = 'data.json') {
|
|
32
|
-
const ext = path$1.extname(basename);
|
|
33
|
-
const name = path$1.basename(basename, ext);
|
|
34
|
-
return [`Content-Disposition: form-data; name="${name}"; filename="${basename}"\r\n` + 'Content-Type: application/json\r\n\r\n', node_stream.Readable.from(JSON.stringify(jsonData), {
|
|
35
|
-
highWaterMark: 1024 * 1024
|
|
36
|
-
}), '\r\n'];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Create and execute a multipart/form-data upload request.
|
|
41
|
-
* Streams large files efficiently with backpressure handling and early server validation.
|
|
42
|
-
*
|
|
43
|
-
* @throws {Error} When network errors occur or stream processing fails
|
|
44
|
-
*/
|
|
45
|
-
async function createUploadRequest(baseUrl, urlPath, requestBodyNoBoundaries, options) {
|
|
46
|
-
// This function constructs and sends a multipart/form-data HTTP POST request
|
|
47
|
-
// where each part is streamed to the server. It supports string payloads
|
|
48
|
-
// and readable streams (e.g., large file uploads).
|
|
49
|
-
|
|
50
|
-
// The body is streamed manually with proper backpressure support to avoid
|
|
51
|
-
// overwhelming Node.js memory (i.e., avoiding out-of-memory crashes for large inputs).
|
|
52
|
-
|
|
53
|
-
// We call `flushHeaders()` early to ensure headers are sent before body transmission
|
|
54
|
-
// begins. If the server rejects the request (e.g., bad org or auth), it will likely
|
|
55
|
-
// respond immediately. We listen for that response while still streaming the body.
|
|
56
|
-
//
|
|
57
|
-
// This protects against cases where the server closes the connection (EPIPE/ECONNRESET)
|
|
58
|
-
// mid-stream, which would otherwise cause hard-to-diagnose failures during file upload.
|
|
59
|
-
//
|
|
60
|
-
// Example failure this mitigates: `socket scan create --org badorg`
|
|
61
|
-
|
|
62
|
-
// eslint-disable-next-line no-async-promise-executor
|
|
63
|
-
return await new Promise(async (pass, fail) => {
|
|
64
|
-
const boundary = `NodeMultipartBoundary${Date.now()}`;
|
|
65
|
-
const boundarySep = `--${boundary}\r\n`;
|
|
66
|
-
const finalBoundary = `--${boundary}--\r\n`;
|
|
67
|
-
const requestBody = [...requestBodyNoBoundaries.flatMap(part => [boundarySep, /* c8 ignore next - Array.isArray branch for part is defensive coding for edge cases. */
|
|
68
|
-
...(Array.isArray(part) ? part : [part])]), finalBoundary];
|
|
69
|
-
const url = new URL(urlPath, baseUrl);
|
|
70
|
-
const req = httpClient.getHttpModule(baseUrl).request(url, {
|
|
71
|
-
method: 'POST',
|
|
72
|
-
...options,
|
|
73
|
-
headers: {
|
|
74
|
-
...options?.headers,
|
|
75
|
-
'Content-Type': `multipart/form-data; boundary=${boundary}`
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Send headers early to prompt server validation (auth, URL, quota, etc.).
|
|
80
|
-
req.flushHeaders();
|
|
81
|
-
|
|
82
|
-
// Concurrently wait for response while we stream body.
|
|
83
|
-
httpClient.getResponse(req).then(pass, fail);
|
|
84
|
-
let aborted = false;
|
|
85
|
-
req.on('error', () => aborted = true);
|
|
86
|
-
req.on('close', () => aborted = true);
|
|
87
|
-
try {
|
|
88
|
-
for (const part of requestBody) {
|
|
89
|
-
/* c8 ignore next 3 - aborted state is difficult to test reliably */
|
|
90
|
-
if (aborted) {
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
if (typeof part === 'string') {
|
|
94
|
-
/* c8 ignore next 5 - backpressure handling requires specific stream conditions */
|
|
95
|
-
if (!req.write(part)) {
|
|
96
|
-
// Wait for 'drain' if backpressure is signaled.
|
|
97
|
-
// eslint-disable-next-line no-await-in-loop
|
|
98
|
-
await events.once(req, 'drain');
|
|
99
|
-
}
|
|
100
|
-
} else if (typeof part?.pipe === 'function') {
|
|
101
|
-
// Stream data chunk-by-chunk with backpressure support.
|
|
102
|
-
const stream = part;
|
|
103
|
-
// eslint-disable-next-line no-await-in-loop
|
|
104
|
-
for await (const chunk of stream) {
|
|
105
|
-
/* c8 ignore next 3 - aborted state during streaming is difficult to test reliably */
|
|
106
|
-
if (aborted) {
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
/* c8 ignore next 3 - backpressure handling requires specific stream conditions */
|
|
110
|
-
if (!req.write(chunk)) {
|
|
111
|
-
await events.once(req, 'drain');
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// Ensure trailing CRLF after file part.
|
|
115
|
-
/* c8 ignore next 4 - trailing CRLF backpressure handling is edge case */
|
|
116
|
-
if (!aborted && !req.write('\r\n')) {
|
|
117
|
-
// eslint-disable-next-line no-await-in-loop
|
|
118
|
-
await events.once(req, 'drain');
|
|
119
|
-
}
|
|
120
|
-
// Cleanup stream to free memory buffers.
|
|
121
|
-
if (typeof part.destroy === 'function') {
|
|
122
|
-
part.destroy();
|
|
123
|
-
}
|
|
124
|
-
/* c8 ignore next 3 - defensive check for non-string/stream types */
|
|
125
|
-
} else {
|
|
126
|
-
throw new TypeError('Expected "string" or "stream" type');
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} catch (e) {
|
|
130
|
-
req.destroy(e);
|
|
131
|
-
fail(e);
|
|
132
|
-
} finally {
|
|
133
|
-
if (!aborted) {
|
|
134
|
-
req.end();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
exports.createRequestBodyForFilepaths = createRequestBodyForFilepaths;
|
|
141
|
-
exports.createRequestBodyForJson = createRequestBodyForJson;
|
|
142
|
-
exports.createUploadRequest = createUploadRequest;
|
package/dist/http-client.js
DELETED
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var http = require('node:http');
|
|
4
|
-
var https = require('node:https');
|
|
5
|
-
var debug = require('@socketsecurity/registry/lib/debug');
|
|
6
|
-
var json = require('@socketsecurity/registry/lib/json');
|
|
7
|
-
var performance = require('@socketsecurity/registry/lib/performance');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @fileoverview HTTP client utilities for Socket API communication.
|
|
11
|
-
* Provides low-level HTTP request handling with proper error management and response parsing.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* HTTP response error for Socket API requests.
|
|
16
|
-
* Extends Error with response details for debugging failed API calls.
|
|
17
|
-
*/
|
|
18
|
-
class ResponseError extends Error {
|
|
19
|
-
/**
|
|
20
|
-
* Create a new ResponseError from an HTTP response.
|
|
21
|
-
* Automatically formats error message with status code and message.
|
|
22
|
-
*/
|
|
23
|
-
constructor(response, message = '') {
|
|
24
|
-
/* c8 ignore next 2 - statusCode and statusMessage may be undefined in edge cases */
|
|
25
|
-
const statusCode = response.statusCode ?? 'unknown';
|
|
26
|
-
const statusMessage = response.statusMessage ?? 'No status message';
|
|
27
|
-
super(/* c8 ignore next - fallback empty message if not provided */
|
|
28
|
-
`Socket API ${message || 'Request failed'} (${statusCode}): ${statusMessage}`);
|
|
29
|
-
this.name = 'ResponseError';
|
|
30
|
-
this.response = response;
|
|
31
|
-
Error.captureStackTrace(this, ResponseError);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Create and execute an HTTP DELETE request.
|
|
37
|
-
* Returns the response stream for further processing.
|
|
38
|
-
*
|
|
39
|
-
* @throws {Error} When network or timeout errors occur
|
|
40
|
-
*/
|
|
41
|
-
async function createDeleteRequest(baseUrl, urlPath, options) {
|
|
42
|
-
const req = getHttpModule(baseUrl).request(`${baseUrl}${urlPath}`, {
|
|
43
|
-
method: 'DELETE',
|
|
44
|
-
...options
|
|
45
|
-
}).end();
|
|
46
|
-
return await getResponse(req);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create and execute an HTTP GET request.
|
|
51
|
-
* Returns the response stream for further processing.
|
|
52
|
-
* Performance tracking enabled with DEBUG=perf.
|
|
53
|
-
*
|
|
54
|
-
* @throws {Error} When network or timeout errors occur
|
|
55
|
-
*/
|
|
56
|
-
async function createGetRequest(baseUrl, urlPath, options) {
|
|
57
|
-
const stopTimer = performance.perfTimer('http:get', {
|
|
58
|
-
urlPath
|
|
59
|
-
});
|
|
60
|
-
try {
|
|
61
|
-
const req = getHttpModule(baseUrl).request(`${baseUrl}${urlPath}`, {
|
|
62
|
-
method: 'GET',
|
|
63
|
-
...options
|
|
64
|
-
}).end();
|
|
65
|
-
const response = await getResponse(req);
|
|
66
|
-
stopTimer({
|
|
67
|
-
statusCode: response.statusCode
|
|
68
|
-
});
|
|
69
|
-
return response;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
stopTimer({
|
|
72
|
-
error: true
|
|
73
|
-
});
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Create and execute an HTTP request with JSON payload.
|
|
80
|
-
* Automatically sets appropriate content headers and serializes the body.
|
|
81
|
-
* Performance tracking enabled with DEBUG=perf.
|
|
82
|
-
*
|
|
83
|
-
* @throws {Error} When network or timeout errors occur
|
|
84
|
-
*/
|
|
85
|
-
async function createRequestWithJson(method, baseUrl, urlPath, json, options) {
|
|
86
|
-
const stopTimer = performance.perfTimer(`http:${method.toLowerCase()}`, {
|
|
87
|
-
urlPath
|
|
88
|
-
});
|
|
89
|
-
try {
|
|
90
|
-
const body = JSON.stringify(json);
|
|
91
|
-
const req = getHttpModule(baseUrl).request(`${baseUrl}${urlPath}`, {
|
|
92
|
-
method,
|
|
93
|
-
...options,
|
|
94
|
-
headers: {
|
|
95
|
-
...options.headers,
|
|
96
|
-
'Content-Length': Buffer.byteLength(body, 'utf8'),
|
|
97
|
-
'Content-Type': 'application/json'
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
req.write(body);
|
|
101
|
-
req.end();
|
|
102
|
-
const response = await getResponse(req);
|
|
103
|
-
stopTimer({
|
|
104
|
-
statusCode: response.statusCode
|
|
105
|
-
});
|
|
106
|
-
return response;
|
|
107
|
-
} catch (error) {
|
|
108
|
-
stopTimer({
|
|
109
|
-
error: true
|
|
110
|
-
});
|
|
111
|
-
throw error;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Read the response body from an HTTP error response.
|
|
117
|
-
* Accumulates all chunks into a complete string for error handling.
|
|
118
|
-
*
|
|
119
|
-
* @throws {Error} When stream errors occur during reading
|
|
120
|
-
*/
|
|
121
|
-
async function getErrorResponseBody(response) {
|
|
122
|
-
return await new Promise((resolve, reject) => {
|
|
123
|
-
let body = '';
|
|
124
|
-
response.setEncoding('utf8');
|
|
125
|
-
response.on('data', chunk => body += chunk);
|
|
126
|
-
response.on('end', () => resolve(body));
|
|
127
|
-
/* c8 ignore next - Extremely rare network or stream error during error response reading. */
|
|
128
|
-
response.on('error', e => reject(e));
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Get the appropriate HTTP module based on URL protocol.
|
|
134
|
-
* Returns http module for http: URLs, https module for https: URLs.
|
|
135
|
-
*/
|
|
136
|
-
function getHttpModule(url) {
|
|
137
|
-
return url.startsWith('https:') ? https : http;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Wait for and return the HTTP response from a request.
|
|
142
|
-
* Handles timeout and error conditions during request processing.
|
|
143
|
-
*
|
|
144
|
-
* @throws {Error} When request times out or network errors occur
|
|
145
|
-
*/
|
|
146
|
-
async function getResponse(req) {
|
|
147
|
-
return await new Promise((resolve, reject) => {
|
|
148
|
-
let timedOut = false;
|
|
149
|
-
req.on('response', response => {
|
|
150
|
-
/* c8 ignore next 3 - Race condition where response arrives after timeout. */
|
|
151
|
-
if (timedOut) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
resolve(response);
|
|
155
|
-
});
|
|
156
|
-
req.on('timeout', () => {
|
|
157
|
-
timedOut = true;
|
|
158
|
-
req.destroy();
|
|
159
|
-
reject(new Error('Request timed out'));
|
|
160
|
-
});
|
|
161
|
-
/* c8 ignore start - Network error handling during request, difficult to test reliably. */
|
|
162
|
-
req.on('error', e => {
|
|
163
|
-
if (!timedOut) {
|
|
164
|
-
reject(e);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
/* c8 ignore stop */
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Parse HTTP response body as JSON.
|
|
173
|
-
* Validates response status and handles empty responses gracefully.
|
|
174
|
-
* Performance tracking enabled with DEBUG=perf.
|
|
175
|
-
*
|
|
176
|
-
* @throws {ResponseError} When response has non-2xx status code
|
|
177
|
-
* @throws {SyntaxError} When response body contains invalid JSON
|
|
178
|
-
*/
|
|
179
|
-
async function getResponseJson(response, method) {
|
|
180
|
-
const stopTimer = performance.perfTimer('http:parse-json');
|
|
181
|
-
try {
|
|
182
|
-
if (!isResponseOk(response)) {
|
|
183
|
-
throw new ResponseError(response, method ? `${method} Request failed` : undefined);
|
|
184
|
-
}
|
|
185
|
-
const responseBody = await getErrorResponseBody(response);
|
|
186
|
-
|
|
187
|
-
// Handle truly empty responses (not whitespace) as valid empty objects.
|
|
188
|
-
if (responseBody === '') {
|
|
189
|
-
debug.debugLog('API response: empty response treated as {}');
|
|
190
|
-
stopTimer({
|
|
191
|
-
success: true
|
|
192
|
-
});
|
|
193
|
-
return {};
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
const responseJson = json.jsonParse(responseBody);
|
|
197
|
-
debug.debugLog('API response:', responseJson);
|
|
198
|
-
stopTimer({
|
|
199
|
-
success: true
|
|
200
|
-
});
|
|
201
|
-
return responseJson;
|
|
202
|
-
} catch (e) {
|
|
203
|
-
stopTimer({
|
|
204
|
-
error: true
|
|
205
|
-
});
|
|
206
|
-
if (e instanceof SyntaxError) {
|
|
207
|
-
// Attach the original response text for better error reporting.
|
|
208
|
-
const enhancedError = new Error(`Socket API - Invalid JSON response:\n${responseBody}\n→ ${e.message}`, {
|
|
209
|
-
cause: e
|
|
210
|
-
});
|
|
211
|
-
enhancedError.name = 'SyntaxError';
|
|
212
|
-
enhancedError.originalResponse = responseBody;
|
|
213
|
-
Object.setPrototypeOf(enhancedError, SyntaxError.prototype);
|
|
214
|
-
throw enhancedError;
|
|
215
|
-
}
|
|
216
|
-
/* c8 ignore start - Error instanceof check and unknown error handling for JSON parsing edge cases. */
|
|
217
|
-
if (e instanceof Error) {
|
|
218
|
-
throw e;
|
|
219
|
-
}
|
|
220
|
-
// Handle non-Error objects thrown by JSON parsing.
|
|
221
|
-
const unknownError = new Error('Unknown JSON parsing error', {
|
|
222
|
-
cause: e
|
|
223
|
-
});
|
|
224
|
-
unknownError.name = 'SyntaxError';
|
|
225
|
-
unknownError.originalResponse = responseBody;
|
|
226
|
-
Object.setPrototypeOf(unknownError, SyntaxError.prototype);
|
|
227
|
-
throw unknownError;
|
|
228
|
-
/* c8 ignore stop */
|
|
229
|
-
}
|
|
230
|
-
} catch (error) {
|
|
231
|
-
stopTimer({
|
|
232
|
-
error: true
|
|
233
|
-
});
|
|
234
|
-
throw error;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Check if HTTP response has a successful status code (2xx range).
|
|
240
|
-
* Returns true for status codes between 200-299, false otherwise.
|
|
241
|
-
*/
|
|
242
|
-
function isResponseOk(response) {
|
|
243
|
-
const {
|
|
244
|
-
statusCode
|
|
245
|
-
} = response;
|
|
246
|
-
/* c8 ignore next - Defensive fallback for edge cases where statusCode might be undefined. */
|
|
247
|
-
return statusCode ? statusCode >= 200 && statusCode < 300 : false;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Transform artifact data based on authentication status.
|
|
252
|
-
* Filters and compacts response data for public/free-tier users.
|
|
253
|
-
*/
|
|
254
|
-
function reshapeArtifactForPublicPolicy(data, isAuthenticated, actions) {
|
|
255
|
-
/* c8 ignore start - Public policy artifact reshaping for unauthenticated users, difficult to test edge cases. */
|
|
256
|
-
// If user is not authenticated, provide a different response structure
|
|
257
|
-
// optimized for the public free-tier experience.
|
|
258
|
-
if (!isAuthenticated) {
|
|
259
|
-
// Parse actions parameter for alert filtering.
|
|
260
|
-
const allowedActions = actions ? actions.split(',') : undefined;
|
|
261
|
-
const reshapeArtifact = artifact => ({
|
|
262
|
-
name: artifact.name,
|
|
263
|
-
version: artifact.version,
|
|
264
|
-
size: artifact.size,
|
|
265
|
-
author: artifact.author,
|
|
266
|
-
type: artifact.type,
|
|
267
|
-
supplyChainRisk: artifact.supplyChainRisk,
|
|
268
|
-
scorecards: artifact.scorecards,
|
|
269
|
-
topLevelAncestors: artifact.topLevelAncestors,
|
|
270
|
-
// Compact the alerts array to reduce response size for non-authenticated
|
|
271
|
-
// requests.
|
|
272
|
-
alerts: artifact.alerts?.filter(alert => {
|
|
273
|
-
// Filter by severity (remove low severity alerts).
|
|
274
|
-
if (alert.severity === 'low') {
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
// Filter by actions if specified.
|
|
278
|
-
if (allowedActions && alert.action && !allowedActions.includes(alert.action)) {
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
return true;
|
|
282
|
-
}).map(alert => ({
|
|
283
|
-
type: alert.type,
|
|
284
|
-
severity: alert.severity,
|
|
285
|
-
key: alert.key
|
|
286
|
-
}))
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
// Handle both single artifacts and objects with artifacts arrays.
|
|
290
|
-
if (data['artifacts']) {
|
|
291
|
-
// Object with artifacts array.
|
|
292
|
-
const artifacts = data['artifacts'];
|
|
293
|
-
return {
|
|
294
|
-
...data,
|
|
295
|
-
artifacts: Array.isArray(artifacts) ? artifacts.map(reshapeArtifact) : artifacts
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
if (data['alerts']) {
|
|
299
|
-
// Single artifact with alerts.
|
|
300
|
-
return reshapeArtifact(data);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return data;
|
|
304
|
-
/* c8 ignore stop */
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Retry helper for HTTP requests with exponential backoff.
|
|
309
|
-
* Wraps any async HTTP function and retries on failure.
|
|
310
|
-
*
|
|
311
|
-
* @param fn - Async function to retry
|
|
312
|
-
* @param retries - Number of retry attempts (default: 0, retries disabled)
|
|
313
|
-
* @param retryDelay - Initial delay in ms (default: 100)
|
|
314
|
-
* @returns Result of the function call
|
|
315
|
-
* @throws {Error} Last error if all retries exhausted
|
|
316
|
-
*/
|
|
317
|
-
async function withRetry(fn, retries = 0, retryDelay = 100) {
|
|
318
|
-
let lastError;
|
|
319
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
320
|
-
try {
|
|
321
|
-
// eslint-disable-next-line no-await-in-loop
|
|
322
|
-
return await fn();
|
|
323
|
-
} catch (error) {
|
|
324
|
-
lastError = error;
|
|
325
|
-
|
|
326
|
-
// Last attempt - throw error with retry context.
|
|
327
|
-
if (attempt === retries) {
|
|
328
|
-
const enhancedError = new Error(`Request failed after ${retries + 1} attempts`, {
|
|
329
|
-
cause: lastError
|
|
330
|
-
});
|
|
331
|
-
throw enhancedError;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Check if error is retryable (network errors, 5xx responses).
|
|
335
|
-
if (error instanceof ResponseError) {
|
|
336
|
-
const status = error.response.statusCode;
|
|
337
|
-
// Don't retry client errors (4xx).
|
|
338
|
-
if (status && status >= 400 && status < 500) {
|
|
339
|
-
throw error;
|
|
340
|
-
}
|
|
341
|
-
debug.debugLog('withRetry', `Retrying after ${status} error (attempt ${attempt + 1}/${retries + 1})`);
|
|
342
|
-
} else {
|
|
343
|
-
debug.debugLog('withRetry', `Retrying after network error (attempt ${attempt + 1}/${retries + 1})`);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Exponential backoff.
|
|
347
|
-
const delayMs = retryDelay * 2 ** attempt;
|
|
348
|
-
debug.debugLog('withRetry', `Waiting ${delayMs}ms before retry`);
|
|
349
|
-
// eslint-disable-next-line no-await-in-loop
|
|
350
|
-
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Fallback error if lastError is somehow undefined.
|
|
355
|
-
/* c8 ignore next - Defensive fallback for undefined lastError */
|
|
356
|
-
throw lastError || new Error('Request failed after retries');
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Create GET request with automatic retry logic.
|
|
361
|
-
* Retries on network errors and 5xx responses.
|
|
362
|
-
*
|
|
363
|
-
* @param retries - Number of retry attempts (default: 0, retries disabled)
|
|
364
|
-
* @param retryDelay - Initial delay in ms (default: 100)
|
|
365
|
-
*/
|
|
366
|
-
async function createGetRequestWithRetry(baseUrl, urlPath, options, retries = 0, retryDelay = 100) {
|
|
367
|
-
return await withRetry(() => createGetRequest(baseUrl, urlPath, options), retries, retryDelay);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Create DELETE request with automatic retry logic.
|
|
372
|
-
* Retries on network errors and 5xx responses.
|
|
373
|
-
*
|
|
374
|
-
* @param retries - Number of retry attempts (default: 0, retries disabled)
|
|
375
|
-
* @param retryDelay - Initial delay in ms (default: 100)
|
|
376
|
-
*/
|
|
377
|
-
async function createDeleteRequestWithRetry(baseUrl, urlPath, options, retries = 0, retryDelay = 100) {
|
|
378
|
-
return await withRetry(() => createDeleteRequest(baseUrl, urlPath, options), retries, retryDelay);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Create request with JSON payload and automatic retry logic.
|
|
383
|
-
* Retries on network errors and 5xx responses.
|
|
384
|
-
*
|
|
385
|
-
* @param retries - Number of retry attempts (default: 0, retries disabled)
|
|
386
|
-
* @param retryDelay - Initial delay in ms (default: 100)
|
|
387
|
-
*/
|
|
388
|
-
async function createRequestWithJsonAndRetry(method, baseUrl, urlPath, json, options, retries = 0, retryDelay = 100) {
|
|
389
|
-
return await withRetry(() => createRequestWithJson(method, baseUrl, urlPath, json, options), retries, retryDelay);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
exports.ResponseError = ResponseError;
|
|
393
|
-
exports.createDeleteRequest = createDeleteRequest;
|
|
394
|
-
exports.createDeleteRequestWithRetry = createDeleteRequestWithRetry;
|
|
395
|
-
exports.createGetRequest = createGetRequest;
|
|
396
|
-
exports.createGetRequestWithRetry = createGetRequestWithRetry;
|
|
397
|
-
exports.createRequestWithJson = createRequestWithJson;
|
|
398
|
-
exports.createRequestWithJsonAndRetry = createRequestWithJsonAndRetry;
|
|
399
|
-
exports.getErrorResponseBody = getErrorResponseBody;
|
|
400
|
-
exports.getHttpModule = getHttpModule;
|
|
401
|
-
exports.getResponse = getResponse;
|
|
402
|
-
exports.getResponseJson = getResponseJson;
|
|
403
|
-
exports.isResponseOk = isResponseOk;
|
|
404
|
-
exports.reshapeArtifactForPublicPolicy = reshapeArtifactForPublicPolicy;
|
|
405
|
-
exports.withRetry = withRetry;
|
package/dist/index.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var constants = require('./constants.js');
|
|
4
|
-
var utils = require('./utils.js');
|
|
5
|
-
var fileUpload = require('./file-upload.js');
|
|
6
|
-
var httpClient = require('./http-client.js');
|
|
7
|
-
var quotaUtils = require('./quota-utils.js');
|
|
8
|
-
var socketSdkClass = require('./socket-sdk-class.js');
|
|
9
|
-
var userAgent = require('./user-agent.js');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @fileoverview Main entry point for the Socket SDK.
|
|
13
|
-
* Provides the SocketSdk class and utility functions for Socket security analysis API interactions.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
exports.DEFAULT_USER_AGENT = constants.DEFAULT_USER_AGENT;
|
|
17
|
-
exports.httpAgentNames = constants.httpAgentNames;
|
|
18
|
-
exports.publicPolicy = constants.publicPolicy;
|
|
19
|
-
exports.normalizeBaseUrl = utils.normalizeBaseUrl;
|
|
20
|
-
exports.promiseWithResolvers = utils.promiseWithResolvers;
|
|
21
|
-
exports.queryToSearchParams = utils.queryToSearchParams;
|
|
22
|
-
exports.resolveAbsPaths = utils.resolveAbsPaths;
|
|
23
|
-
exports.resolveBasePath = utils.resolveBasePath;
|
|
24
|
-
exports.createRequestBodyForFilepaths = fileUpload.createRequestBodyForFilepaths;
|
|
25
|
-
exports.createRequestBodyForJson = fileUpload.createRequestBodyForJson;
|
|
26
|
-
exports.createUploadRequest = fileUpload.createUploadRequest;
|
|
27
|
-
exports.ResponseError = httpClient.ResponseError;
|
|
28
|
-
exports.createDeleteRequest = httpClient.createDeleteRequest;
|
|
29
|
-
exports.createGetRequest = httpClient.createGetRequest;
|
|
30
|
-
exports.createRequestWithJson = httpClient.createRequestWithJson;
|
|
31
|
-
exports.getErrorResponseBody = httpClient.getErrorResponseBody;
|
|
32
|
-
exports.getHttpModule = httpClient.getHttpModule;
|
|
33
|
-
exports.getResponse = httpClient.getResponse;
|
|
34
|
-
exports.getResponseJson = httpClient.getResponseJson;
|
|
35
|
-
exports.isResponseOk = httpClient.isResponseOk;
|
|
36
|
-
exports.reshapeArtifactForPublicPolicy = httpClient.reshapeArtifactForPublicPolicy;
|
|
37
|
-
exports.calculateTotalQuotaCost = quotaUtils.calculateTotalQuotaCost;
|
|
38
|
-
exports.getAllMethodRequirements = quotaUtils.getAllMethodRequirements;
|
|
39
|
-
exports.getMethodRequirements = quotaUtils.getMethodRequirements;
|
|
40
|
-
exports.getMethodsByPermissions = quotaUtils.getMethodsByPermissions;
|
|
41
|
-
exports.getMethodsByQuotaCost = quotaUtils.getMethodsByQuotaCost;
|
|
42
|
-
exports.getQuotaCost = quotaUtils.getQuotaCost;
|
|
43
|
-
exports.getQuotaUsageSummary = quotaUtils.getQuotaUsageSummary;
|
|
44
|
-
exports.getRequiredPermissions = quotaUtils.getRequiredPermissions;
|
|
45
|
-
exports.hasQuotaForMethods = quotaUtils.hasQuotaForMethods;
|
|
46
|
-
exports.SocketSdk = socketSdkClass.SocketSdk;
|
|
47
|
-
exports.createUserAgentFromPkgJson = userAgent.createUserAgentFromPkgJson;
|