@budibase/backend-core 2.7.7-alpha.7 → 2.7.7-alpha.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@budibase/backend-core",
3
- "version": "2.7.7-alpha.7",
3
+ "version": "2.7.7-alpha.8",
4
4
  "description": "Budibase backend core libraries used in server and worker",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "@budibase/nano": "10.1.2",
24
24
  "@budibase/pouchdb-replication-stream": "1.2.10",
25
- "@budibase/types": "2.7.7-alpha.7",
25
+ "@budibase/types": "2.7.7-alpha.8",
26
26
  "@shopify/jest-koa-mocks": "5.0.1",
27
27
  "@techpass/passport-openidconnect": "0.3.2",
28
28
  "aws-cloudfront-sign": "2.2.0",
@@ -101,5 +101,5 @@
101
101
  }
102
102
  }
103
103
  },
104
- "gitHead": "9d9022ad8b10316acdce141458bfb434140cdde6"
104
+ "gitHead": "f64e9dbe99612a5c43f962cd95309fde1431cbad"
105
105
  }
@@ -1,12 +1,17 @@
1
1
  import crypto from "crypto"
2
+ import fs from "fs"
3
+ import zlib from "zlib"
2
4
  import env from "../environment"
5
+ import { join } from "path"
3
6
 
4
7
  const ALGO = "aes-256-ctr"
5
8
  const SEPARATOR = "-"
6
9
  const ITERATIONS = 10000
7
- const RANDOM_BYTES = 16
8
10
  const STRETCH_LENGTH = 32
9
11
 
12
+ const SALT_LENGTH = 16
13
+ const IV_LENGTH = 16
14
+
10
15
  export enum SecretOption {
11
16
  API = "api",
12
17
  ENCRYPTION = "encryption",
@@ -31,15 +36,15 @@ export function getSecret(secretOption: SecretOption): string {
31
36
  return secret
32
37
  }
33
38
 
34
- function stretchString(string: string, salt: Buffer) {
35
- return crypto.pbkdf2Sync(string, salt, ITERATIONS, STRETCH_LENGTH, "sha512")
39
+ function stretchString(secret: string, salt: Buffer) {
40
+ return crypto.pbkdf2Sync(secret, salt, ITERATIONS, STRETCH_LENGTH, "sha512")
36
41
  }
37
42
 
38
43
  export function encrypt(
39
44
  input: string,
40
45
  secretOption: SecretOption = SecretOption.API
41
46
  ) {
42
- const salt = crypto.randomBytes(RANDOM_BYTES)
47
+ const salt = crypto.randomBytes(SALT_LENGTH)
43
48
  const stretched = stretchString(getSecret(secretOption), salt)
44
49
  const cipher = crypto.createCipheriv(ALGO, stretched, salt)
45
50
  const base = cipher.update(input)
@@ -60,3 +65,115 @@ export function decrypt(
60
65
  const final = decipher.final()
61
66
  return Buffer.concat([base, final]).toString()
62
67
  }
68
+
69
+ export async function encryptFile(
70
+ { dir, filename }: { dir: string; filename: string },
71
+ secret: string
72
+ ) {
73
+ const outputFileName = `${filename}.enc`
74
+
75
+ const filePath = join(dir, filename)
76
+ const inputFile = fs.createReadStream(filePath)
77
+ const outputFile = fs.createWriteStream(join(dir, outputFileName))
78
+
79
+ const salt = crypto.randomBytes(SALT_LENGTH)
80
+ const iv = crypto.randomBytes(IV_LENGTH)
81
+ const stretched = stretchString(secret, salt)
82
+ const cipher = crypto.createCipheriv(ALGO, stretched, iv)
83
+
84
+ outputFile.write(salt)
85
+ outputFile.write(iv)
86
+
87
+ inputFile.pipe(zlib.createGzip()).pipe(cipher).pipe(outputFile)
88
+
89
+ return new Promise<{ filename: string; dir: string }>(r => {
90
+ outputFile.on("finish", () => {
91
+ r({
92
+ filename: outputFileName,
93
+ dir,
94
+ })
95
+ })
96
+ })
97
+ }
98
+
99
+ async function getSaltAndIV(path: string) {
100
+ const fileStream = fs.createReadStream(path)
101
+
102
+ const salt = await readBytes(fileStream, SALT_LENGTH)
103
+ const iv = await readBytes(fileStream, IV_LENGTH)
104
+ fileStream.close()
105
+ return { salt, iv }
106
+ }
107
+
108
+ export async function decryptFile(
109
+ inputPath: string,
110
+ outputPath: string,
111
+ secret: string
112
+ ) {
113
+ const { salt, iv } = await getSaltAndIV(inputPath)
114
+ const inputFile = fs.createReadStream(inputPath, {
115
+ start: SALT_LENGTH + IV_LENGTH,
116
+ })
117
+
118
+ const outputFile = fs.createWriteStream(outputPath)
119
+
120
+ const stretched = stretchString(secret, salt)
121
+ const decipher = crypto.createDecipheriv(ALGO, stretched, iv)
122
+
123
+ const unzip = zlib.createGunzip()
124
+
125
+ inputFile.pipe(decipher).pipe(unzip).pipe(outputFile)
126
+
127
+ return new Promise<void>((res, rej) => {
128
+ outputFile.on("finish", () => {
129
+ outputFile.close()
130
+ res()
131
+ })
132
+
133
+ inputFile.on("error", e => {
134
+ outputFile.close()
135
+ rej(e)
136
+ })
137
+
138
+ decipher.on("error", e => {
139
+ outputFile.close()
140
+ rej(e)
141
+ })
142
+
143
+ unzip.on("error", e => {
144
+ outputFile.close()
145
+ rej(e)
146
+ })
147
+
148
+ outputFile.on("error", e => {
149
+ outputFile.close()
150
+ rej(e)
151
+ })
152
+ })
153
+ }
154
+
155
+ function readBytes(stream: fs.ReadStream, length: number) {
156
+ return new Promise<Buffer>((resolve, reject) => {
157
+ let bytesRead = 0
158
+ const data: Buffer[] = []
159
+
160
+ stream.on("readable", () => {
161
+ let chunk
162
+
163
+ while ((chunk = stream.read(length - bytesRead)) !== null) {
164
+ data.push(chunk)
165
+ bytesRead += chunk.length
166
+ }
167
+
168
+ resolve(Buffer.concat(data))
169
+ })
170
+
171
+ stream.on("end", () => {
172
+ reject(new Error("Insufficient data in the stream."))
173
+ })
174
+
175
+ stream.on("error", error => {
176
+ reject(error)
177
+ })
178
+ })
179
+ }