snowflake-sdk
Advanced tools
Comparing version 1.9.1 to 1.9.2
/* | ||
* Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> | ||
* Copyright (c) 2015-2023 Snowflake Computing Inc. All rights reserved. | ||
*/ | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var net = require('net'); | ||
var tls = require('tls'); | ||
var url = require('url'); | ||
var extend = require('extend'); | ||
var HttpsProxyAgent = require('https-proxy-agent'); | ||
var createAgent = require('agent-base'); | ||
var inherits = require('util').inherits; | ||
var debug = require('debug')('https-proxy-agent'); | ||
var SocketUtil = require('./socket_util'); | ||
const tls = require('tls'); | ||
const { HttpsProxyAgent } = require('https-proxy-agent'); | ||
const SocketUtil = require('./socket_util'); | ||
const Logger = require('../logger'); | ||
/** | ||
* Module exports. | ||
*/ | ||
module.exports = createHttpsProxyAgent; | ||
module.exports = HttpsProxyOcspAgent; | ||
/** | ||
* The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to the | ||
* specified "HTTP(s) proxy server" in order to proxy HTTPS requests. | ||
* | ||
* @api public | ||
*/ | ||
function HttpsProxyOcspAgent(opts) | ||
{ | ||
if (!(this instanceof HttpsProxyOcspAgent)) | ||
{ | ||
return new HttpsProxyOcspAgent(opts); | ||
} | ||
if ('string' == typeof opts) | ||
{ | ||
opts = url.parse(opts); | ||
} | ||
if (!opts) | ||
{ | ||
throw new Error('an HTTP(S) proxy server `host` and `port` must be specified!'); | ||
} | ||
debug('creating new HttpsProxyAgent instance: %o', opts); | ||
var HttpsAgent = new HttpsProxyAgent(opts); | ||
var Agent = createAgent.call(this, connect); | ||
HttpsAgent.callback = Agent.callback; | ||
HttpsAgent.timeout = Agent.timeout; | ||
HttpsAgent.options = Agent.opts; | ||
var proxy = extend({}, opts); | ||
// if `true`, then connect to the proxy server over TLS. defaults to `false`. | ||
HttpsAgent.secureProxy = proxy.protocol ? /^https:?$/i.test(proxy.protocol) : false; | ||
// prefer `hostname` over `host`, and set the `port` if needed | ||
proxy.host = proxy.hostname || proxy.host; | ||
proxy.port = +proxy.port || (this.secureProxy ? 443 : 80); | ||
if (proxy.host && proxy.path) | ||
{ | ||
// if both a `host` and `path` are specified then it's most likely the | ||
// result of a `url.parse()` call... we need to remove the `path` portion so | ||
// that `net.connect()` doesn't attempt to open that as a unix socket file. | ||
delete proxy.path; | ||
delete proxy.pathname; | ||
} | ||
if (proxy.user && proxy.password) | ||
{ | ||
// user:password | ||
proxy.auth = proxy.user + ':' + proxy.password; | ||
delete proxy.user; | ||
delete proxy.password; | ||
if (!HttpsAgent.secureProxy) | ||
{ | ||
Logger.getInstance().warn("Warning: connecting to an authenticated proxy server through HTTP. To use HTTPS, set 'proxyProtocol' to 'HTTPS'") | ||
} | ||
} | ||
HttpsAgent.proxy = proxy; | ||
return HttpsAgent; | ||
function createHttpsProxyAgent(opts) { | ||
// HttpsProxyAgent >= 6.x takes two arguments for its constructor | ||
// See: https://github.com/TooTallNate/proxy-agents/blob/main/packages/https-proxy-agent/CHANGELOG.md#600 | ||
const { host: hostname, port, user: username, password, protocol: rawProtocol, ...agentOptions } = opts; | ||
const protocol = rawProtocol.endsWith(':') ? rawProtocol : `${rawProtocol}:`; | ||
return new HttpsProxyOcspAgent({ hostname, port, username, password, protocol }, agentOptions); | ||
} | ||
inherits(HttpsProxyAgent, createAgent); | ||
/** | ||
* Called when the node-core HTTP client library is creating a new HTTP request. | ||
* | ||
* @api public | ||
*/ | ||
function connect(req, opts, fn) | ||
{ | ||
var proxy = this.proxy; | ||
var agent = this; | ||
Logger.getInstance().debug("Using proxy=%s for host %s", proxy.host, opts.host); | ||
// create a socket connection to the proxy server | ||
var socket; | ||
if (this.secureProxy) | ||
{ | ||
socket = tls.connect(proxy); | ||
class HttpsProxyOcspAgent extends HttpsProxyAgent { | ||
constructor(proxy, opts) { | ||
super(proxy, opts); | ||
} | ||
else | ||
{ | ||
socket = net.connect(proxy); | ||
} | ||
// we need to buffer any HTTP traffic that happens with the proxy before we get | ||
// the CONNECT response, so that if the response is anything other than an "200" | ||
// response code, then we can re-play the "data" events on the socket once the | ||
// HTTP parser is hooked up... | ||
var buffers = []; | ||
var buffersLength = 0; | ||
function read() | ||
{ | ||
var b = socket.read(); | ||
if (b) | ||
{ | ||
ondata(b); | ||
async connect(req, opts) { | ||
Logger.getInstance().debug('Using proxy=%s for host %s', this.proxy.hostname, opts.host); | ||
const socket = await super.connect(req, opts); | ||
if (socket instanceof tls.TLSSocket) { | ||
SocketUtil.secureSocket(socket, opts.host, this); | ||
} | ||
else | ||
{ | ||
socket.once('readable', read); | ||
} | ||
return socket; | ||
} | ||
function cleanup() | ||
{ | ||
socket.removeListener('data', ondata); | ||
socket.removeListener('end', onend); | ||
socket.removeListener('error', onerror); | ||
socket.removeListener('close', onclose); | ||
socket.removeListener('readable', read); | ||
} | ||
function onclose(err) | ||
{ | ||
debug('onclose had error %o', err); | ||
} | ||
function onend() | ||
{ | ||
debug('onend'); | ||
} | ||
function onerror(err) | ||
{ | ||
cleanup(); | ||
fn(err); | ||
} | ||
function ondata(b) | ||
{ | ||
buffers.push(b); | ||
buffersLength += b.length; | ||
var buffered = Buffer.concat(buffers, buffersLength); | ||
var str = buffered.toString('ascii'); | ||
if (!~str.indexOf('\r\n\r\n')) | ||
{ | ||
// keep buffering | ||
debug('have not received end of HTTP headers yet...'); | ||
if (socket.read) | ||
{ | ||
read(); | ||
} | ||
else | ||
{ | ||
socket.once('data', ondata); | ||
} | ||
return; | ||
} | ||
var firstLine = str.substring(0, str.indexOf('\r\n')); | ||
var statusCode = +firstLine.split(' ')[1]; | ||
debug('got proxy server response: %o', firstLine); | ||
if (200 === statusCode) | ||
{ | ||
// 200 Connected status code! | ||
var sock = socket; | ||
// nullify the buffered data since we won't be needing it | ||
buffers = buffered = null; | ||
if (opts.secureEndpoint) | ||
{ | ||
var host = opts.host; | ||
// since the proxy is connecting to an SSL server, we have | ||
// to upgrade this socket connection to an SSL connection | ||
debug('upgrading proxy-connected socket to TLS connection: %o', opts.host); | ||
opts.socket = socket; | ||
opts.servername = opts.host; | ||
opts.host = null; | ||
opts.hostname = null; | ||
opts.port = null; | ||
sock = tls.connect(opts); | ||
// pass in proxy agent to apply proxy for ocsp connection | ||
// ocsp connection won't be secureEndpoint so no worry for recursive | ||
// ocsp validation | ||
SocketUtil.secureSocket(sock, host, agent); | ||
} | ||
cleanup(); | ||
fn(null, sock); | ||
} | ||
else | ||
{ | ||
// some other status code that's not 200... need to re-play the HTTP header | ||
// "data" events onto the socket once the HTTP machinery is attached so that | ||
// the user can parse and handle the error status code | ||
cleanup(); | ||
// save a reference to the concat'd Buffer for the `onsocket` callback | ||
buffers = buffered; | ||
// need to wait for the "socket" event to re-play the "data" events | ||
req.once('socket', onsocket); | ||
fn(null, socket); | ||
} | ||
} | ||
function onsocket(socket) | ||
{ | ||
// replay the "buffers" Buffer onto the `socket`, since at this point | ||
// the HTTP module machinery has been hooked up for the user | ||
if ('function' == typeof socket.ondata) | ||
{ | ||
// node <= v0.11.3, the `ondata` function is set on the socket | ||
socket.ondata(buffers, 0, buffers.length); | ||
} | ||
else if (socket.listeners('data').length > 0) | ||
{ | ||
// node > v0.11.3, the "data" event is listened for directly | ||
socket.emit('data', buffers); | ||
} | ||
else | ||
{ | ||
// never? | ||
throw new Error('should not happen...'); | ||
} | ||
// nullify the cached Buffer instance | ||
buffers = null; | ||
} | ||
socket.on('error', onerror); | ||
socket.on('close', onclose); | ||
socket.on('end', onend); | ||
if (socket.read) | ||
{ | ||
read(); | ||
} | ||
else | ||
{ | ||
socket.once('data', ondata); | ||
} | ||
var hostname = opts.host + ':' + opts.port; | ||
var msg = 'CONNECT ' + hostname + ' HTTP/1.1\r\n'; | ||
var auth = proxy.auth; | ||
if (auth) | ||
{ | ||
msg += 'Proxy-Authorization: Basic ' + Buffer.from(auth, 'utf8').toString('base64') + '\r\n'; | ||
} | ||
msg += 'Host: ' + hostname + '\r\n' + | ||
'Connection: close\r\n' + | ||
'\r\n'; | ||
socket.write(msg); | ||
} |
@@ -206,3 +206,3 @@ /* | ||
var proxyPassword = options.proxyPassword; | ||
var proxyProtocol = options.proxyProtocol; | ||
var proxyProtocol = options.proxyProtocol || 'http'; | ||
var noProxy = options.noProxy; | ||
@@ -271,2 +271,3 @@ | ||
port: proxyPort, | ||
protocol: proxyProtocol, | ||
noProxy: noProxy | ||
@@ -273,0 +274,0 @@ }; |
@@ -39,2 +39,3 @@ /* | ||
HttpClient.prototype.request = function (options) { | ||
let request; | ||
const requestOptions = prepareRequestOptions.call(this, options); | ||
@@ -41,0 +42,0 @@ let sendRequest = async function sendRequest() { |
@@ -56,2 +56,12 @@ /* | ||
Logger.getInstance().trace(`Create and add to cache new agent ${agentId}`); | ||
// detect and log PROXY envvar + agent proxy settings | ||
const compareAndLogEnvAndAgentProxies = Util.getCompareAndLogEnvAndAgentProxies(agentOptions); | ||
Logger.getInstance().debug(`Proxy settings used in requests:${compareAndLogEnvAndAgentProxies.messages}`); | ||
// if there's anything to warn on (e.g. both envvar + agent proxy used, and they are different) | ||
// log warnings on them | ||
if (compareAndLogEnvAndAgentProxies.warnings) { | ||
Logger.getInstance().warn(`${compareAndLogEnvAndAgentProxies.warnings}`); | ||
} | ||
return agent; | ||
@@ -58,0 +68,0 @@ } |
@@ -73,2 +73,7 @@ /* | ||
{ | ||
// if we're running in DEBUG loglevel, probably we want to see the full error too | ||
const logErr = err ? JSON.stringify(err, Util.getCircularReplacer()) | ||
: `status: ${JSON.stringify(response.status)} ${JSON.stringify(response.statusText)}` | ||
+ ` headers: ${JSON.stringify(response.headers)}`; | ||
Logger.getInstance().debug('Encountered an error when getting data from cloud storage: ' + logErr); | ||
// if we haven't exceeded the maximum number of retries yet and the | ||
@@ -88,3 +93,3 @@ // server came back with a retryable error code. | ||
const nextSendRequestWaitTimeMs = sleep * 1000; | ||
Logger.getInstance().trace("Request will be retried after %d milliseconds", nextSendRequestWaitTimeMs); | ||
Logger.getInstance().trace("Request will be retried after %d milliseconds", Math.floor(nextSendRequestWaitTimeMs)); | ||
setTimeout(sendRequest, nextSendRequestWaitTimeMs); | ||
@@ -91,0 +96,0 @@ return |
@@ -618,2 +618,6 @@ /* | ||
{ | ||
// if we're running in DEBUG loglevel, probably we want to see the full error instead | ||
Logger.getInstance().debug('Encountered an error when sending the request. Details: ' | ||
+ JSON.stringify(err, Util.getCircularReplacer())); | ||
err = Errors.createNetworkError( | ||
@@ -620,0 +624,0 @@ ErrorCodes.ERR_SF_NETWORK_COULD_NOT_CONNECT, err); |
@@ -640,2 +640,71 @@ /* | ||
return subdomainRegex.test(value); | ||
}; | ||
/** | ||
* Try to get the PROXY environmental variables | ||
* On Windows, envvar name is case-insensitive, but on *nix, it's case-sensitive | ||
* | ||
* Compare them with the proxy specified on the Connection, if any | ||
* Return with the log constructed from the components detection and comparison | ||
* If there's something to warn the user about, return that too | ||
* | ||
* @param the agentOptions object from agent creation | ||
* @returns {object} | ||
*/ | ||
exports.getCompareAndLogEnvAndAgentProxies = function(agentOptions) { | ||
const envProxy = {}; | ||
let logMessages = {'messages': '', 'warnings': ''}; | ||
envProxy.httpProxy = process.env.HTTP_PROXY || process.env.http_proxy; | ||
envProxy.httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy; | ||
envProxy.noProxy = process.env.NO_PROXY || process.env.no_proxy; | ||
envProxy.logHttpProxy = envProxy.httpProxy ? | ||
'HTTP_PROXY: ' + envProxy.httpProxy : 'HTTP_PROXY: <unset>'; | ||
envProxy.logHttpsProxy = envProxy.httpsProxy ? | ||
'HTTPS_PROXY: ' + envProxy.httpsProxy : 'HTTPS_PROXY: <unset>'; | ||
envProxy.logNoProxy = envProxy.noProxy ? | ||
'NO_PROXY: ' + envProxy.noProxy : 'NO_PROXY: <unset>'; | ||
// log PROXY envvars | ||
if (envProxy.httpProxy || envProxy.httpsProxy) { | ||
logMessages.messages = logMessages.messages + ` // PROXY environment variables: ` | ||
+ `${envProxy.logHttpProxy} ${envProxy.logHttpsProxy} ${envProxy.logNoProxy}.`; | ||
} | ||
// log proxy config on Connection, if any set | ||
if (agentOptions.host) { | ||
const proxyHostAndPort = agentOptions.host + ':' + agentOptions.port | ||
const proxyProtocolHostAndPort = agentOptions.protocol ? | ||
' protocol=' + agentOptions.protocol + ' proxy=' + proxyHostAndPort | ||
: ' proxy=' + proxyHostAndPort; | ||
const proxyUsername = agentOptions.user ? ' user=' + agentOptions.user : '' | ||
logMessages.messages = logMessages.messages + ` // Proxy configured in Connection:${proxyProtocolHostAndPort}${proxyUsername}`; | ||
// check if both the PROXY envvars and Connection proxy config is set | ||
// generate warnings if they are, and are also different | ||
if (envProxy.httpProxy && | ||
this.removeScheme(envProxy.httpProxy).toLowerCase() != this.removeScheme(proxyHostAndPort).toLowerCase()) { | ||
logMessages.warnings = logMessages.warnings + ` Using both the HTTP_PROXY (${envProxy.httpProxy})` | ||
+` and the proxyHost:proxyPort (${proxyHostAndPort}) settings to connect, but with different values.` | ||
+` If you experience connectivity issues, try unsetting one of them.` | ||
}; | ||
if (envProxy.httpsProxy && | ||
this.removeScheme(envProxy.httpsProxy).toLowerCase() != this.removeScheme(proxyHostAndPort).toLowerCase()) { | ||
logMessages.warnings = logMessages.warnings + ` Using both the HTTPS_PROXY (${envProxy.httpsProxy})` | ||
+` and the proxyHost:proxyPort (${proxyHostAndPort}) settings to connect, but with different values.` | ||
+` If you experience connectivity issues, try unsetting one of them.` | ||
}; | ||
} | ||
logMessages.messages = logMessages.messages ? logMessages.messages : ' none.' | ||
return logMessages; | ||
}; | ||
/** | ||
* remove http:// or https:// from the input, e.g. used with proxy URL | ||
* @param input | ||
* @returns {string} | ||
*/ | ||
exports.removeScheme = function (input) { | ||
return input.toString().replace(/(^\w+:|^)\/\//, ''); | ||
}; |
{ | ||
"name": "snowflake-sdk", | ||
"version": "1.9.1", | ||
"version": "1.9.2", | ||
"description": "Node.js driver for Snowflake", | ||
@@ -25,4 +25,4 @@ "dependencies": { | ||
"generic-pool": "^3.8.2", | ||
"glob": "^7.1.6", | ||
"https-proxy-agent": "^5.0.1", | ||
"glob": "^9.0.0", | ||
"https-proxy-agent": "^7.0.2", | ||
"jsonwebtoken": "^9.0.0", | ||
@@ -29,0 +29,0 @@ "mime-types": "^2.1.29", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 6 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
8
537223
17110
36
+ Added@aws-sdk/client-s3@3.703.0(transitive)
+ Addedaxios@1.7.8(transitive)
+ Addedbrace-expansion@2.0.1(transitive)
+ Addedglob@9.3.5(transitive)
+ Addedlru-cache@10.4.3(transitive)
+ Addedminimatch@8.0.4(transitive)
+ Addedminipass@4.2.87.1.2(transitive)
+ Addedpath-scurry@1.11.1(transitive)
- Removed@aws-sdk/client-s3@3.705.0(transitive)
- Removedaxios@1.7.9(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedglob@7.2.3(transitive)
- Removedinflight@1.0.6(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
Updatedglob@^9.0.0
Updatedhttps-proxy-agent@^7.0.2