nodejs - escaping variable character within .env - javascript

My .env has, say, two simple variables:
USERNAME:"myusername"
PASSWORD:"mypassword&7"
The thing is, when I try to use shell.exec to pass a git clone command, it seems to be ignoring the '&7'from my password variable.
shell.exec(`git clone https://${process.env.USERNAME}:${process.env.USERNAME}#github.com/my-repo/xyz-git-ops.git`);
it outputs:
/bin/sh: 7#gmy-repo/xyz-git-ops.git: No such file or directory
Cloning into 'mypassword'...
fatal: unable to access 'https://myusername:mypassword/': URL using bad/illegal format or missing URL
I notice a few weird stuff:
1 - it ignores the last 2 characters of my password value, the '&7'and the git clone output replaces it with a '/'instead.
2 - if I do console.log(process.env.USERNAME), it prints the value perfectly: mypassword&7
All that makes me wonder if is there a way of either escaping the '&' char from the password value or if my approach to pass credential via shell.exec() is absolutely mistaken. Bellow is the full content of my .js file
const nodeCron = require("node-cron");
const shell = require('shelljs');
const rpath = '/Users/myuser/Documents/Git Ops Cron/repos';
require('dotenv').config();
const start = Date.now();
const username = process.env.USERNAME
const password = process.env.PASSWORD
async function xyzGitOps(){
console.log("Running scheduled job", start);
shell.cd(rpath);
shell.exec(`git clone https://${username}:${password}#github.com/my-repo/xyz-git-ops.git`);
return console.log("Job finished");
}
const job = nodeCron.schedule("* * * * *", xyzGitOps);

The username/password component of a URL should be percent encoded.
The node:url URL class will do this for you
const repo = new URL(`https://github.com/my-repo/xyz-git-ops.git`)
repo.username = process.env.USERNAME
repo.password = process.env.PASSWORD
The URL's .toString() encodes the values:
> String(repo)
'https://userw:pass%%2F%40%23$#github.com/my-repo/xyz-git-ops.git'
> `${repo}`
'https://userw:pass%%2F%40%23$#github.com/my-repo/xyz-git-ops.git'

Related

Unable to verify x-slack-signature

I've been having a slack app for a few years already. It was used exclusively in only one workspace.
On receiving a slack command I run a regular auth check as it described in slack API.
My code:
const requestSignatureHeader = req.headers["x-slack-signature"] as string;
const requestTimestamp = req.headers["x-slack-request-timestamp"];
const hmac = crypto.createHmac("sha256", this.signingSecret);
const [version, requestSignature] = requestSignatureHeader.split("=");
const rawBody = stringify(req.body)
.replace(/\+/g, "%2B")
.replace(/%20/g, "+")
.replace(/!/g, "%21")
.replace(/'/g, "%27")
.replace(/:/g, "%3A")
.replace(/,/g, "%2C")
.replace(/#/g, "%40")
.replace(/\//g, "%2F");
const stringToSign = `${version}:${requestTimestamp}:${rawBody}`;
hmac.update(stringToSign);
const calculatedSignature = hmac.digest("hex");
const isValid = requestSignature === calculatedSignature;
It was working correctly until a few weeks ago we've moved to the enterprise workspace (it maybe just a coincidence though).
Right now x-slack-signature is always different from the calculated value. I've checked multiple times: signing secret is the same that specified in app's settings. Any ideas what could be the problem?

How to get raw output from SHA1 using JS as PHP does?

I'm trying to dynamically generate a security header at Postman pre-request script. To do so, I need to transform the following code snippet from PHP to JS.
$password = "SECRETPASSWORD";
$nonce = random_bytes(32);
date_default_timezone_set("UTC");
$created = date(DATE_ATOM);
$encodedNonce = base64_encode($nonce);
$passwordHash = base64_encode(sha1($nonce . $created . sha1($password, true), true));
(Note the true flag at php's sha1() function, forcing raw output).
I've coded this code snippet so far:
var uuid = require('uuid');
var CryptoJS = require('crypto-js');
var moment = require('moment');
// Generate messageId
var messageId = uuid.v4();
pm.environment.set('messageId', messageId);
// Generate nonce
var nonce = uuid.v4();
var encodedNonce = CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(nonce)
);
pm.environment.set('nonce', encodedNonce);
// Generate created
var created = moment().utc().format();
pm.environment.set('created', created);
// Generate password hash
var password = 'SECRETPASSWORD';
var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");
var passwordHash = CryptoJS.SHA1(nonce + created + rawSha1Password).toString(CryptoJS.enc.Base64);
pm.environment.set('passwordHash', passwordHash);
My JS script is almost working, the only problem seems to be the sha1 generation. Taking the following example values:
password: SECRETPASSWORD
nonce: 55d61876-f882-42f0-b390-dc662a7e7279
created: 2021-01-21T18:19:32Z
The output from PHP is:
encodedNonce: NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash: olI18mUowhmeCwjb1FJNHtTHYDA=
But, the output from JS is:
encodedNonce: NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash: tk/uYkL/3Uq0oIkYO0nlBGnV/0E=
As you can see, the encodedNonce is built correctly; however the passwordHash value is different. As I'm using Postman, I have a limited JS libraries available.
Taking this into account, how can I get the same result as the PHP one?
In the line
var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");
the password hash is read into a buffer and then UTF-8 decoded. The UTF-8 decoding generally corrupts the data, see here. A possible solution is to concatenate the WordArrays instead of the strings:
function getPasswordHash(test){
// Generate nonce
var nonceWA = !test ? CryptoJS.lib.WordArray.random(32) : CryptoJS.enc.Utf8.parse('55d61876-f882-42f0-b390-dc662a7e7279');
console.log('nonce (Base64): ' + nonceWA.toString(CryptoJS.enc.Base64));
// Generate created
var created = !test ? moment().utc().format('YYYY-MM-DDTHH:mm:ss[Z]') : '2021-01-21T18:19:32Z';
var createdWA = CryptoJS.enc.Utf8.parse(created);
console.log('created: ' + created);
// Hash password
var pwd = 'SECRETPASSWORD';
var pwdHashWA = CryptoJS.SHA1(pwd);
// Hash nonce + created + pwd
var passwordHash = CryptoJS.SHA1(nonceWA.concat(createdWA).concat(pwdHashWA)).toString(CryptoJS.enc.Base64);
console.log('passwordHash: ' + passwordHash);
}
getPasswordHash(true); // with testdata
getPasswordHash(false); // without testdata
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
When the code is executed, the first call of getPasswordHash() uses the test data for nonce and date (test=true), the second call applies a random nonce and the current date (test=false) . The call with the test data returns the same result as the PHP code:
nonce (Base64): NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
created: 2021-01-21T18:19:32Z
passwordHash: olI18mUowhmeCwjb1FJNHtTHYDA=
CryptoJS implements CryptoJS.lib.WordArray.random() as CSPRNG, which is the counterpart of the PHP method random_bytes(). The use of the uuid library is therefore actually not necessary. I have chosen CryptoJS.lib.WordArray.random() in my example because it is closest to the PHP code.

trying to decompress xref stream from pdf - getting "ERROR incorrect header check"

I am trying to parse the xref stream from PDF in JavaScript. I managed to succesfully isolate the stream itself (I checked that it's ok by comparing it in debugging mode with the value between steram. and endstream tags in PDF.
However, when I try to inflate it using pako lib, I get an error saying: ERROR incorrect header check.
The compression method is FlateDecode, which can be seen from the dictionary.
Here is the code in question:
const dict = pdfStr.slice(pdf.startXRef);
const xrefStreamStart = this.getSubstringIndex(dict, 'stream', 1) + 'stream'.length + 2;
const xrefStreamEnd = this.getSubstringIndex(dict, 'endstream', 1) + 1;
const xrefStream = dict.slice(xrefStreamStart, xrefStreamEnd);
const inflatedXrefStream = pako.inflate(this.str2ab(xrefStream), { to: 'string' });
pdfStr is the whole PDF read as a string, while *pdf.startXRef* holds the value of the position of the xref stream object.
Here's the whole PDF if someone wants to have a look: https://easyupload.io/lzf9he
EDIT: As mcernak has suggested I had a problem that I included /r and /n in the stream. However, now that I corrected the code I got a different error: invalid distance too far back
The stream content is located between stream\r\n and \r\nendstream.
You need to take into account those two additional characters (\r\n) at the beginning and at the end to read the correct data:
const dict = pdfStr.slice(pdf.startXRef);
const xrefStreamStart = this.getSubstringIndex(dict, 'stream', 1) + 'stream'.length + 2;
const xrefStreamEnd = this.getSubstringIndex(dict, 'endstream', 1) - 2;
const xrefStream = dict.slice(xrefStreamStart, xrefStreamEnd);
const inflatedXrefStream = pako.inflate(this.str2ab(xrefStream), { to: 'string' });

Python body.encode( ) Javascript alternative

I'm trying to verify a webhook coming from Plaid in NodeJS by calculating the Sha256 of the webhook body and I'm following a Python code here where the code is showing :
# Compute the has of the body.
m = hashlib.sha256()
m.update(body.encode())
body_hash = m.hexdigest()
What's the alternative of body.encode() in Javascript before passing it to the Sha256 function please ? Note that the body I'm getting is an object containing the following data :
{ error: null, item_id: '4zAGyokJ1XiWP63QNl1RuLZV76o55nudVXzNG',
new_transactions: 0, webhook_code: 'DEFAULT_UPDATE', webhook_type:
'TRANSACTIONS' }
However I'm trying to get this hash :
b05ef560b59e8d8e427433c5e0f6a11579b5dfe6534257558b896f858007385a
So, if the body is JSON (NOT JSON STRING) then you need to stringify it and put it in the .update function As the m.body takes a string. If you have your body as STRING then just put it in as is.
This is from the Crypto Example here:
const crypto = require('crypto');
const hash = crypto.createHash('sha256');
const stringBody = JSON.stringify(body);
hash.update(stringBody);
console.log(hash.digest('hex'));
Edit:
If the hash is not same then maybe you need to correct the newlines or whitespaces. You need to make both bodies exactly the same. Here In the below example I am using same exact string and encoding using Python AND NodeJS.
import hashlib
body = '{"error":null,"item_id":"4zAGyokJ1XiWP63QNl1RuLZV76o55nudVXzNG","new_transactions":0,"webhook_code":"DEFAULT_UPDATE","webhook_type":"TRANSACTIONS"}'
m = hashlib.sha256()
m.update(body.encode())
body_hash = m.hexdigest()
print(body_hash)
Output:
python3 file.py
26f1120ccaf99a383b7462b233e18994d0c06d4585e3fe9a91a449e97a1c03ba
And Using NodeJS:
const crypto = require('crypto');
const hash = crypto.createHash('sha256');
const body = {
error: null,
item_id: '4zAGyokJ1XiWP63QNl1RuLZV76o55nudVXzNG',
new_transactions: 0,
webhook_code: 'DEFAULT_UPDATE',
webhook_type: 'TRANSACTIONS'
}
const stringBody = JSON.stringify(body);
hash.update(stringBody);
console.log(hash.digest('hex'));
Output:
node file.js
26f1120ccaf99a383b7462b233e18994d0c06d4585e3fe9a91a449e97a1c03ba

Is there any way to create mongodb like _id strings without mongodb?

I really like the format of the _ids generated by mongodb. Mostly because I can pull data like the date out of them client side. I'm planning to use another database but still want that type of _id for my document. How can I create these ids without using mongodb?
Thanks!
A very easy pseudo ObjectId generator in javascript:
const ObjectId = (m = Math, d = Date, h = 16, s = s => m.floor(s).toString(h)) =>
s(d.now() / 1000) + ' '.repeat(h).replace(/./g, () => s(m.random() * h))
Use the official MongoDB BSON lib in the client
I have a browser client that generates ObjectIds. I wanted to make sure that I employ the same ObjectId algorithm in the client as the one used in the server. MongoDB has js-bson which can be used to accomplish that.
If you are using javascript with node.
npm install --save bson
Using require statement
var ObjectID = require('bson').ObjectID;
var id = new ObjectID();
console.log(id.toString());
Using ES6 import statement
import { ObjectID } from 'bson';
const id = new ObjectID();
console.log(id.toString());
The library also lets you import using good old script tags but I have not tried this.
Object IDs are usually generated by the client, so any MongoDB driver would have code to generate them.
If you're looking for JavaScript, here's some code from the MongoDB Node.js driver:
https://github.com/mongodb/js-bson/blob/1.0-branch/lib/bson/objectid.js
And another, simpler solution:
https://github.com/justaprogrammer/ObjectId.js
Extending Rubin Stolk's and ChrisV's answer in a more readable syntax (KISS).
function objectId () {
return hex(Date.now() / 1000) +
' '.repeat(16).replace(/./g, () => hex(Math.random() * 16))
}
function hex (value) {
return Math.floor(value).toString(16)
}
export default objectId
ruben-stolk's answer is great, but deliberately opaque? Very slightly easier to pick apart is:
const ObjectId = (rnd = r16 => Math.floor(r16).toString(16)) =>
rnd(Date.now()/1000) + ' '.repeat(16).replace(/./g, () => rnd(Math.random()*16));
(actually in slightly fewer characters). Kudos though!
This is a simple function to generate a new objectId
newObjectId() {
const timestamp = Math.floor(new Date().getTime() / 1000).toString(16);
const objectId = timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, () => {
return Math.floor(Math.random() * 16).toString(16);
}).toLowerCase();
return objectId;
}
Here's a link! to a library to do that.
https://www.npmjs.com/package/mongo-object-reader
You can read and write hexadecimal strings.
const { createObjectID, readObjectID,isValidObjectID } = require('mongo-object-reader');
//Creates a new immutable `ObjectID` instance based on the current system time.
const ObjectID = createObjectID() //a valid 24 character `ObjectID` hex string.
//returns boolean
// input - a valid 24 character `ObjectID` hex string.
const isValid = isValidObjectID(ObjectID)
//returns an object with data
// input - a valid 24 character `ObjectID` hex string.
const objectData = readObjectID(ObjectID)
console.log(ObjectID) //ObjectID
console.log(isValid) // true
console.log(objectData) /*
{ ObjectID: '5e92d4be2ced3f58d92187f5',
timeStamp:
{ hex: '5e92d4be',
value: 1586681022,
createDate: 1970-01-19T08:44:41.022Z },
random: { hex: '2ced3f58d9', value: 192958912729 },
incrementValue: { hex: '2187f5', value: 2197493 } }
*/
There is a detailed specification here
http://www.mongodb.org/display/DOCS/Object+IDs
Which you can use to roll your own id strings

Categories

Resources