@@ -5,3 +5,2 @@ 'use strict' | ||
| var EventEmitter = require('events').EventEmitter | ||
| var util = require('util') | ||
@@ -8,0 +7,0 @@ const { parse, serialize } = require('pg-protocol') |
+2
-0
@@ -7,2 +7,3 @@ 'use strict' | ||
| var Pool = require('pg-pool') | ||
| const { DatabaseError } = require('pg-protocol') | ||
@@ -25,2 +26,3 @@ const poolFactory = (Client) => { | ||
| this.types = require('pg-types') | ||
| this.DatabaseError = DatabaseError | ||
| } | ||
@@ -27,0 +29,0 @@ |
+108
-50
@@ -23,7 +23,15 @@ 'use strict' | ||
| } | ||
| if (typeof password !== 'string') { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string') | ||
| } | ||
| if (typeof serverData !== 'string') { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string') | ||
| } | ||
| const sv = extractVariablesFromFirstServerMessage(serverData) | ||
| const sv = parseServerFirstMessage(serverData) | ||
| if (!sv.nonce.startsWith(session.clientNonce)) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce') | ||
| } else if (sv.nonce.length === session.clientNonce.length) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short') | ||
| } | ||
@@ -35,4 +43,4 @@ | ||
| var clientKey = createHMAC(saltedPassword, 'Client Key') | ||
| var storedKey = crypto.createHash('sha256').update(clientKey).digest() | ||
| var clientKey = hmacSha256(saltedPassword, 'Client Key') | ||
| var storedKey = sha256(clientKey) | ||
@@ -46,8 +54,8 @@ var clientFirstMessageBare = 'n=*,r=' + session.clientNonce | ||
| var clientSignature = createHMAC(storedKey, authMessage) | ||
| var clientSignature = hmacSha256(storedKey, authMessage) | ||
| var clientProofBytes = xorBuffers(clientKey, clientSignature) | ||
| var clientProof = clientProofBytes.toString('base64') | ||
| var serverKey = createHMAC(saltedPassword, 'Server Key') | ||
| var serverSignatureBytes = createHMAC(serverKey, authMessage) | ||
| var serverKey = hmacSha256(saltedPassword, 'Server Key') | ||
| var serverSignatureBytes = hmacSha256(serverKey, authMessage) | ||
@@ -63,15 +71,8 @@ session.message = 'SASLResponse' | ||
| } | ||
| if (typeof serverData !== 'string') { | ||
| throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: serverData must be a string') | ||
| } | ||
| var serverSignature | ||
| const { serverSignature } = parseServerFinalMessage(serverData) | ||
| String(serverData) | ||
| .split(',') | ||
| .forEach(function (part) { | ||
| switch (part[0]) { | ||
| case 'v': | ||
| serverSignature = part.substr(2) | ||
| break | ||
| } | ||
| }) | ||
| if (serverSignature !== session.serverSignature) { | ||
@@ -82,32 +83,72 @@ throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match') | ||
| function extractVariablesFromFirstServerMessage(data) { | ||
| var nonce, salt, iteration | ||
| /** | ||
| * printable = %x21-2B / %x2D-7E | ||
| * ;; Printable ASCII except ",". | ||
| * ;; Note that any "printable" is also | ||
| * ;; a valid "value". | ||
| */ | ||
| function isPrintableChars(text) { | ||
| if (typeof text !== 'string') { | ||
| throw new TypeError('SASL: text must be a string') | ||
| } | ||
| return text | ||
| .split('') | ||
| .map((_, i) => text.charCodeAt(i)) | ||
| .every((c) => (c >= 0x21 && c <= 0x2b) || (c >= 0x2d && c <= 0x7e)) | ||
| } | ||
| String(data) | ||
| .split(',') | ||
| .forEach(function (part) { | ||
| switch (part[0]) { | ||
| case 'r': | ||
| nonce = part.substr(2) | ||
| break | ||
| case 's': | ||
| salt = part.substr(2) | ||
| break | ||
| case 'i': | ||
| iteration = parseInt(part.substr(2), 10) | ||
| break | ||
| /** | ||
| * base64-char = ALPHA / DIGIT / "/" / "+" | ||
| * | ||
| * base64-4 = 4base64-char | ||
| * | ||
| * base64-3 = 3base64-char "=" | ||
| * | ||
| * base64-2 = 2base64-char "==" | ||
| * | ||
| * base64 = *base64-4 [base64-3 / base64-2] | ||
| */ | ||
| function isBase64(text) { | ||
| return /^(?:[a-zA-Z0-9+/]{4})*(?:[a-zA-Z0-9+/]{2}==|[a-zA-Z0-9+/]{3}=)?$/.test(text) | ||
| } | ||
| function parseAttributePairs(text) { | ||
| if (typeof text !== 'string') { | ||
| throw new TypeError('SASL: attribute pairs text must be a string') | ||
| } | ||
| return new Map( | ||
| text.split(',').map((attrValue) => { | ||
| if (!/^.=/.test(attrValue)) { | ||
| throw new Error('SASL: Invalid attribute pair entry') | ||
| } | ||
| const name = attrValue[0] | ||
| const value = attrValue.substring(2) | ||
| return [name, value] | ||
| }) | ||
| ) | ||
| } | ||
| function parseServerFirstMessage(data) { | ||
| const attrPairs = parseAttributePairs(data) | ||
| const nonce = attrPairs.get('r') | ||
| if (!nonce) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing') | ||
| } else if (!isPrintableChars(nonce)) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters') | ||
| } | ||
| const salt = attrPairs.get('s') | ||
| if (!salt) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing') | ||
| } else if (!isBase64(salt)) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64') | ||
| } | ||
| if (!iteration) { | ||
| const iterationText = attrPairs.get('i') | ||
| if (!iterationText) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing') | ||
| } else if (!/^[1-9][0-9]*$/.test(iterationText)) { | ||
| throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count') | ||
| } | ||
| const iteration = parseInt(iterationText, 10) | ||
@@ -121,19 +162,36 @@ return { | ||
| function parseServerFinalMessage(serverData) { | ||
| const attrPairs = parseAttributePairs(serverData) | ||
| const serverSignature = attrPairs.get('v') | ||
| if (!serverSignature) { | ||
| throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing') | ||
| } else if (!isBase64(serverSignature)) { | ||
| throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature must be base64') | ||
| } | ||
| return { | ||
| serverSignature, | ||
| } | ||
| } | ||
| function xorBuffers(a, b) { | ||
| if (!Buffer.isBuffer(a)) a = Buffer.from(a) | ||
| if (!Buffer.isBuffer(b)) b = Buffer.from(b) | ||
| var res = [] | ||
| if (a.length > b.length) { | ||
| for (var i = 0; i < b.length; i++) { | ||
| res.push(a[i] ^ b[i]) | ||
| } | ||
| } else { | ||
| for (var j = 0; j < a.length; j++) { | ||
| res.push(a[j] ^ b[j]) | ||
| } | ||
| if (!Buffer.isBuffer(a)) { | ||
| throw new TypeError('first argument must be a Buffer') | ||
| } | ||
| return Buffer.from(res) | ||
| if (!Buffer.isBuffer(b)) { | ||
| throw new TypeError('second argument must be a Buffer') | ||
| } | ||
| if (a.length !== b.length) { | ||
| throw new Error('Buffer lengths must match') | ||
| } | ||
| if (a.length === 0) { | ||
| throw new Error('Buffers cannot be empty') | ||
| } | ||
| return Buffer.from(a.map((_, i) => a[i] ^ b[i])) | ||
| } | ||
| function createHMAC(key, msg) { | ||
| function sha256(text) { | ||
| return crypto.createHash('sha256').update(text).digest() | ||
| } | ||
| function hmacSha256(key, msg) { | ||
| return crypto.createHmac('sha256', key).update(msg).digest() | ||
@@ -143,6 +201,6 @@ } | ||
| function Hi(password, saltBytes, iterations) { | ||
| var ui1 = createHMAC(password, Buffer.concat([saltBytes, Buffer.from([0, 0, 0, 1])])) | ||
| var ui1 = hmacSha256(password, Buffer.concat([saltBytes, Buffer.from([0, 0, 0, 1])])) | ||
| var ui = ui1 | ||
| for (var i = 0; i < iterations - 1; i++) { | ||
| ui1 = createHMAC(password, ui1) | ||
| ui1 = hmacSha256(password, ui1) | ||
| ui = xorBuffers(ui, ui1) | ||
@@ -149,0 +207,0 @@ } |
+1
-1
| MIT License | ||
| Copyright (c) 2010 - 2020 Brian Carlson | ||
| Copyright (c) 2010 - 2021 Brian Carlson | ||
@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy |
+7
-6
| { | ||
| "name": "pg", | ||
| "version": "8.5.1", | ||
| "version": "8.6.0", | ||
| "description": "PostgreSQL client - pure javascript & libpq with the same API", | ||
@@ -17,3 +17,4 @@ "keywords": [ | ||
| "type": "git", | ||
| "url": "git://github.com/brianc/node-postgres.git" | ||
| "url": "git://github.com/brianc/node-postgres.git", | ||
| "directory": "packages/pg" | ||
| }, | ||
@@ -25,5 +26,5 @@ "author": "Brian Carlson <brian.m.carlson@gmail.com>", | ||
| "packet-reader": "1.0.0", | ||
| "pg-connection-string": "^2.4.0", | ||
| "pg-pool": "^3.2.2", | ||
| "pg-protocol": "^1.4.0", | ||
| "pg-connection-string": "^2.5.0", | ||
| "pg-pool": "^3.3.0", | ||
| "pg-protocol": "^1.5.0", | ||
| "pg-types": "^2.1.0", | ||
@@ -57,3 +58,3 @@ "pgpass": "1.x" | ||
| }, | ||
| "gitHead": "0b9bb349dcb10f6473737001062082b65efc74be" | ||
| "gitHead": "d45947938263bec30a1e3252452f04177b785f66" | ||
| } |
+5
-0
@@ -56,2 +56,7 @@ # node-postgres | ||
| </a> | ||
| <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAQAAAB0m0auAAAADElEQVR42mNkIBIAAABSAAI2VLqiAAAAAElFTkSuQmCC" /> | ||
| <a href="https://www.eaze.com" target="_blank"> | ||
| <img height="80" src="https://node-postgres.com/eaze.png" /> | ||
| </a> | ||
| </div> | ||
@@ -58,0 +63,0 @@ |
Network access
Supply chain riskThis module accesses the network.
Found 3 instances 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
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 3 instances 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
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
71301
3.99%2009
2.92%102
5.15%Updated
Updated
Updated