92 lines
3.4 KiB
JavaScript
92 lines
3.4 KiB
JavaScript
|
|
// Shared Ivanti / RiskSense API helpers
|
||
|
|
// Centralizes HTTP calls so ivantiWorkflows.js, ivantiFindings.js, and
|
||
|
|
// ivantiFpWorkflow.js all use the same implementation.
|
||
|
|
|
||
|
|
const https = require('https');
|
||
|
|
|
||
|
|
const IVANTI_URL_BASE = 'https://platform4.risksense.com/api/v1';
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// JSON POST — used for search, workflow creation, etc.
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
function ivantiPost(urlPath, body, apiKey, skipTls) {
|
||
|
|
const bodyStr = JSON.stringify(body);
|
||
|
|
const fullUrl = new URL(IVANTI_URL_BASE + urlPath);
|
||
|
|
|
||
|
|
return new Promise((resolve, reject) => {
|
||
|
|
const options = {
|
||
|
|
hostname: fullUrl.hostname,
|
||
|
|
path: fullUrl.pathname + fullUrl.search,
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'accept': '*/*',
|
||
|
|
'content-type': 'application/json',
|
||
|
|
'x-api-key': apiKey,
|
||
|
|
'x-http-client-type': 'browser',
|
||
|
|
'content-length': Buffer.byteLength(bodyStr)
|
||
|
|
},
|
||
|
|
rejectUnauthorized: !skipTls,
|
||
|
|
timeout: 15000
|
||
|
|
};
|
||
|
|
|
||
|
|
const req = https.request(options, (res) => {
|
||
|
|
let data = '';
|
||
|
|
res.on('data', (chunk) => { data += chunk; });
|
||
|
|
res.on('end', () => resolve({ status: res.statusCode, body: data }));
|
||
|
|
});
|
||
|
|
|
||
|
|
req.on('timeout', () => req.destroy(new Error('Request timed out')));
|
||
|
|
req.on('error', reject);
|
||
|
|
req.write(bodyStr);
|
||
|
|
req.end();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Multipart POST — used for file attachment uploads.
|
||
|
|
// Constructs multipart/form-data manually using Node's https module.
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
function ivantiMultipartPost(urlPath, fileBuffer, fileName, apiKey, skipTls) {
|
||
|
|
const boundary = '----IvantiUpload' + Date.now().toString(36) + Math.random().toString(36).slice(2);
|
||
|
|
const fullUrl = new URL(IVANTI_URL_BASE + urlPath);
|
||
|
|
|
||
|
|
// Build multipart body
|
||
|
|
const preamble = Buffer.from(
|
||
|
|
`--${boundary}\r\n` +
|
||
|
|
`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n` +
|
||
|
|
`Content-Type: application/octet-stream\r\n\r\n`
|
||
|
|
);
|
||
|
|
const epilogue = Buffer.from(`\r\n--${boundary}--\r\n`);
|
||
|
|
const bodyBuffer = Buffer.concat([preamble, fileBuffer, epilogue]);
|
||
|
|
|
||
|
|
return new Promise((resolve, reject) => {
|
||
|
|
const options = {
|
||
|
|
hostname: fullUrl.hostname,
|
||
|
|
path: fullUrl.pathname + fullUrl.search,
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'accept': '*/*',
|
||
|
|
'content-type': `multipart/form-data; boundary=${boundary}`,
|
||
|
|
'x-api-key': apiKey,
|
||
|
|
'x-http-client-type': 'browser',
|
||
|
|
'content-length': bodyBuffer.length
|
||
|
|
},
|
||
|
|
rejectUnauthorized: !skipTls,
|
||
|
|
timeout: 30000
|
||
|
|
};
|
||
|
|
|
||
|
|
const req = https.request(options, (res) => {
|
||
|
|
let data = '';
|
||
|
|
res.on('data', (chunk) => { data += chunk; });
|
||
|
|
res.on('end', () => resolve({ status: res.statusCode, body: data }));
|
||
|
|
});
|
||
|
|
|
||
|
|
req.on('timeout', () => req.destroy(new Error('Request timed out')));
|
||
|
|
req.on('error', reject);
|
||
|
|
req.write(bodyBuffer);
|
||
|
|
req.end();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = { IVANTI_URL_BASE, ivantiPost, ivantiMultipartPost };
|