@ethernity-cloud/sdk-js 1.2.0 → 1.5.0

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.
@@ -1,201 +1,211 @@
1
- const fs = require("fs");
2
- const elliptic = require('elliptic');
3
- const crypto = require('crypto');
4
- const rs = require('jsrsasign');
5
- const ECKey = require('ec-key');
6
- const curve = new elliptic.ec('p384');
7
- const ascii85 = require('ascii85');
8
- const ethSigUtil = require('@metamask/eth-sig-util');
9
- const {createHash} = require('crypto');
10
-
11
- function sha256(content) {
12
- return createHash('sha256').update(content).digest('hex');
13
- }
14
-
15
- function fileChecksum(file) {
16
- const fileContent = fs.readFileSync(file, 'utf8');
17
- return sha256(fileContent);
18
- }
19
-
20
- async function fileChecksumFromSwiftStream(swiftStreamClient, bucketName, fileName) {
21
- const fileContent = await swiftStreamClient.getFileContent(bucketName, fileName);
22
- //console.log(fileContent);
23
- return sha256(fileContent);
24
- }
25
-
26
- const getPEMFromPublicFile = (publicKeyPath) => {
27
- const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
28
- return crypto.createPublicKey(publicKey).export({type: 'spki', format: 'pem'});
29
- }
30
-
31
- const getPEMFromPublic = (publicKey) => {
32
- return crypto.createPublicKey(publicKey).export({type: 'spki', format: 'pem'});
33
- }
34
-
35
- const loadCertificate = (certificate) => {
36
- //console.log(certificate)
37
- const publicKeyPEM = getPEMFromPublic(certificate);
38
- const key = new ECKey(publicKeyPEM, 'pem');
39
- //console.log(key);
40
- return key;
41
- }
42
-
43
- function getPrivatePointFromPrivateKey(privateKeyFile) {
44
- let privateKey = fs.readFileSync(privateKeyFile, 'utf8');
45
- //console.log(privateKey);
46
- let privateKeyHex = rs.KEYUTIL.getKey(privateKey).prvKeyHex;
47
- const privateKeyObject = curve.keyFromPrivate(privateKeyHex, 'hex');
48
- return privateKeyObject.getPrivate();
49
- }
50
-
51
- const generatePublicKey = (x, y) => {
52
- const pub = {x: x, y};
53
- return curve.keyFromPublic(pub, 'hex');
54
- }
55
-
56
- function getPublicPointFromCoords(x, y) {
57
- const publicKey = generatePublicKey(x, y);
58
- return publicKey.getPublic();
59
- }
60
-
61
- function ecc_point_to_256_bit_key(point) {
62
- const value = point.getX().toString() + point.getY().toString();
63
- const hash = crypto.createHash('sha256');
64
- hash.update(value);
65
- return hash.digest();
66
- }
67
-
68
- function encryptedDataToBase64Json(encryptedMsg) {
69
- const key = curve.keyFromPublic(encryptedMsg.cipherTextPublicKey, 'hex');
70
- const jsonObj = {
71
- ciphertext: encryptedMsg.ciphertext.toString('hex'),
72
- nonce: encryptedMsg.nonce.toString('hex'),
73
- authTag: encryptedMsg.authTag.toString('hex'),
74
- x: key.getPublic().getX(),
75
- y: key.getPublic().getY()
76
- };
77
- const jsonString = JSON.stringify(jsonObj);
78
- const binaryData = Buffer.from(jsonString, 'utf8');
79
- return binaryData.toString('base64');
80
- }
81
-
82
- function encryptedDataFromBase64Json(base64Data) {
83
- // decode the base64-encoded string back to a JSON string
84
- const decodedJsonData = Buffer.from(base64Data, 'base64').toString('utf8');
85
- return JSON.parse(decodedJsonData);
86
- }
87
-
88
-
89
- function encrypt(certificate, message) {
90
- const key = loadCertificate(certificate);
91
- return encrypt_ecc(message, generatePublicKey(key.x, key.y));
92
- }
93
-
94
- function encrypt_ecc(msg, publicKey) {
95
- const cipherTextPrivateKey = crypto.randomBytes(32);
96
- const sharedEccKey = publicKey.getPublic().mul(cipherTextPrivateKey);
97
- const secretKey = ecc_point_to_256_bit_key(sharedEccKey);
98
- const iv = crypto.randomBytes(16);
99
- const cipher = crypto.createCipheriv('aes-256-gcm', secretKey, iv);
100
- let encrypted = cipher.update(msg, 'utf8', 'hex');
101
- encrypted += cipher.final('hex');
102
- const cipherTextPublicKey = curve.g.mul(cipherTextPrivateKey);
103
- return {
104
- ciphertext: encrypted,
105
- secretKey: secretKey,
106
- nonce: iv,
107
- authTag: cipher.getAuthTag().toString("hex"),
108
- cipherTextPublicKey: cipherTextPublicKey.encode('hex')
109
- };
110
- }
111
-
112
- function decrypt(privateKeyFile, encryptedMsg) {
113
- //console.log(privateKeyFile);
114
- const privatePoint = getPrivatePointFromPrivateKey(privateKeyFile);
115
- return decrypt_ecc(encryptedMsg, privatePoint);
116
- }
117
-
118
- function decrypt_ecc(encryptedMsg, privatePoint) {
119
- try {
120
- const {ciphertext, nonce, authTag, x, y} = encryptedMsg;
121
- const publicPoint = getPublicPointFromCoords(`${x}`, `${y}`);
122
- const sharedEccKey = publicPoint.mul(privatePoint);
123
- const secretKey = ecc_point_to_256_bit_key(sharedEccKey);
124
-
125
- const decipher = crypto.createDecipheriv('aes-256-gcm', secretKey, Buffer.from(nonce, 'hex'));
126
- decipher.setAuthTag(Buffer.from(authTag, 'hex'));
127
- let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
128
- decrypted += decipher.final('utf8');
129
- return decrypted;
130
- } catch (e) {
131
- console.log(`Error while decrypting ECC:`, e.message);
132
- return false;
133
- }
134
- }
135
-
136
- function hexToBytes(hexStr) {
137
- if (hexStr.startsWith("0x")) {
138
- hexStr = hexStr.slice(2);
139
- }
140
- return Buffer.from(hexStr, "hex");
141
- }
142
-
143
- function encryptWithPublicKey(publicKey, data) {
144
- // Returned object contains 4 properties: version, ephemPublicKey, nonce, ciphertext
145
- // Each contains data encoded using base64, version is always the same string
146
- const enc = ethSigUtil.encrypt({
147
- publicKey: hexToBytes(publicKey).toString('base64'),
148
- data: ascii85.encode(Buffer.from(data)).toString(),
149
- version: 'x25519-xsalsa20-poly1305',
150
- });
151
-
152
- // We want to store the data in smart contract, therefore we concatenate them
153
- // into single Buffer
154
- const buf = Buffer.concat([
155
- Buffer.from(enc.ephemPublicKey, 'base64'),
156
- Buffer.from(enc.nonce, 'base64'),
157
- Buffer.from(enc.ciphertext, 'base64'),
158
- ]);
159
-
160
- // In smart contract we are using `bytes[112]` variable (fixed size byte array)
161
- // you might need to use `bytes` type for dynamic sized array
162
- // We are also using ethers.js which requires type `number[]` when passing data
163
- // for argument of type `bytes` to the smart contract function
164
- // Next line just converts the buffer to `number[]` required by contract function
165
- // THIS LINE IS USED IN OUR ORIGINAL CODE:
166
- // return buf.toJSON().data;
167
-
168
- // Return just the Buffer to make the function directly compatible with decryptData function
169
- return buf;
170
- }
171
-
172
- // (async () => {
173
- // try {
174
- // const message = 'testabc1';
175
- // console.log('initial message:', message);
176
- // const publicKeyPEM = getPEMFromPublic('./app/cert1-ca1-clean.crt');
177
- // const ECKey = require('ec-key');
178
- // const key = new ECKey(publicKeyPEM, 'pem');
179
- // const encrypted_message = encrypt(key.x, key.y, message);
180
- // console.log(encrypted_message);
181
- // console.log(encryptedDataToBase64Json(encrypted_message));
182
- // //
183
- // // const encrypted_message = 'eyJjaXBoZXJ0ZXh0IjoiZTYzZTcyNDY0MWY4YWRmN2QyNjNmMTI0ZGYxZTdlYzZmNGM5NGU1NCIsIm5vbmNlIjoiYmE0YWU5YjMyOWFhZWRiM2ViZTM5MGU4YTY3YWFiNmMiLCJhdXRoVGFnIjoiOGU2NmI4NTVmNTE3N2E0NzZlMjkwNTE3Y2Q2ZDE3ZmUiLCJ4IjoiN2E1MjhhNGI5YzAzODM4Mjc0ZmFiYzVlM2RiOWQ2ZWFiNzBiZTFiMDA3YWJiOTcyYzI0YmFhNGE1ZmI4OGJjNTVmNDVlNGNkMjZjY2NhNTFhOTk3OGZkNDRhZmE1YjQ0IiwieSI6IjE5M2NjMWYwMzVmOGMxZjEzNGU4MTEzOGIyOTRiODBiZWQyYjkyMzdkZmIyOTMyOGRjOTg1MzlmMjhkNjIzMTJiYTQ2OTU2NDFkYjIyNjk0ZWUyNGFjZjQ4MjBiZDlhMSJ9';
184
- // // const messageDecrypted = decrypt('./app/cert1-ca1-clean.key', encryptedDataToBase64Json(encrypted_message));
185
- // // console.log('decrypted message:', messageDecrypted);
186
- // } catch (e) {
187
- // console.log(e);
188
- // }
189
- //
190
- // })();
191
-
192
- module.exports = {
193
- encryptedDataFromBase64Json,
194
- encryptedDataToBase64Json,
195
- encryptWithPublicKey,
196
- decrypt,
197
- encrypt,
198
- sha256,
199
- fileChecksum,
200
- fileChecksumFromSwiftStream
201
- };
1
+ const fs = require("fs");
2
+ const elliptic = require('elliptic');
3
+ const crypto = require('crypto');
4
+ const rs = require('jsrsasign');
5
+ const ECKey = require('ec-key');
6
+ const curve = new elliptic.ec('p384');
7
+ const ascii85 = require('ascii85');
8
+ const ethSigUtil = require('@metamask/eth-sig-util');
9
+ const {createHash} = require('crypto');
10
+
11
+ function sha256(content) {
12
+ return createHash('sha256').update(content).digest('hex');
13
+ }
14
+
15
+ function fileChecksum(file) {
16
+ const fileContent = fs.readFileSync(file, 'utf8');
17
+ return sha256(fileContent);
18
+ }
19
+
20
+ async function fileChecksumFromSwiftStream(swiftStreamClient, bucketName, fileName) {
21
+ const fileContent = await swiftStreamClient.getFileContent(bucketName, fileName);
22
+ //console.log(fileContent);
23
+ return sha256(fileContent);
24
+ }
25
+
26
+ const getPEMFromPublicFile = (publicKeyPath) => {
27
+ const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
28
+ return crypto.createPublicKey(publicKey).export({type: 'spki', format: 'pem'});
29
+ }
30
+
31
+ const getPEMFromPublic = (publicKey) => {
32
+ return crypto.createPublicKey(publicKey).export({type: 'spki', format: 'pem'});
33
+ }
34
+
35
+ const loadCertificate = (certificate) => {
36
+ //console.log(certificate)
37
+ const publicKeyPEM = getPEMFromPublic(certificate);
38
+ const key = new ECKey(publicKeyPEM, 'pem');
39
+ //console.log(key);
40
+ return key;
41
+ }
42
+
43
+ // Accepts EITHER a path to a PEM file or the PEM text itself.
44
+ //
45
+ // mainnet keeps passing a path: CAS injects the key into key_file after
46
+ // attestation, and that flow is unchanged. On testnet the enclave self-derives
47
+ // its key and now holds it only in memory (never writes it), so it passes the
48
+ // PEM straight through -- nothing about the key ever reaches the filesystem.
49
+ // Detection is on the PEM header, not on file existence, so a path that does
50
+ // not exist still fails loudly instead of being mistaken for key material.
51
+ function getPrivatePointFromPrivateKey(privateKeyOrFile) {
52
+ const value = String(privateKeyOrFile || '');
53
+ const privateKey = value.includes('-----BEGIN')
54
+ ? value
55
+ : fs.readFileSync(value, 'utf8');
56
+ let privateKeyHex = rs.KEYUTIL.getKey(privateKey).prvKeyHex;
57
+ const privateKeyObject = curve.keyFromPrivate(privateKeyHex, 'hex');
58
+ return privateKeyObject.getPrivate();
59
+ }
60
+
61
+ const generatePublicKey = (x, y) => {
62
+ const pub = {x: x, y};
63
+ return curve.keyFromPublic(pub, 'hex');
64
+ }
65
+
66
+ function getPublicPointFromCoords(x, y) {
67
+ const publicKey = generatePublicKey(x, y);
68
+ return publicKey.getPublic();
69
+ }
70
+
71
+ function ecc_point_to_256_bit_key(point) {
72
+ const value = point.getX().toString() + point.getY().toString();
73
+ const hash = crypto.createHash('sha256');
74
+ hash.update(value);
75
+ return hash.digest();
76
+ }
77
+
78
+ function encryptedDataToBase64Json(encryptedMsg) {
79
+ const key = curve.keyFromPublic(encryptedMsg.cipherTextPublicKey, 'hex');
80
+ const jsonObj = {
81
+ ciphertext: encryptedMsg.ciphertext.toString('hex'),
82
+ nonce: encryptedMsg.nonce.toString('hex'),
83
+ authTag: encryptedMsg.authTag.toString('hex'),
84
+ x: key.getPublic().getX(),
85
+ y: key.getPublic().getY()
86
+ };
87
+ const jsonString = JSON.stringify(jsonObj);
88
+ const binaryData = Buffer.from(jsonString, 'utf8');
89
+ return binaryData.toString('base64');
90
+ }
91
+
92
+ function encryptedDataFromBase64Json(base64Data) {
93
+ // decode the base64-encoded string back to a JSON string
94
+ const decodedJsonData = Buffer.from(base64Data, 'base64').toString('utf8');
95
+ return JSON.parse(decodedJsonData);
96
+ }
97
+
98
+
99
+ function encrypt(certificate, message) {
100
+ const key = loadCertificate(certificate);
101
+ return encrypt_ecc(message, generatePublicKey(key.x, key.y));
102
+ }
103
+
104
+ function encrypt_ecc(msg, publicKey) {
105
+ const cipherTextPrivateKey = crypto.randomBytes(32);
106
+ const sharedEccKey = publicKey.getPublic().mul(cipherTextPrivateKey);
107
+ const secretKey = ecc_point_to_256_bit_key(sharedEccKey);
108
+ const iv = crypto.randomBytes(16);
109
+ const cipher = crypto.createCipheriv('aes-256-gcm', secretKey, iv);
110
+ let encrypted = cipher.update(msg, 'utf8', 'hex');
111
+ encrypted += cipher.final('hex');
112
+ const cipherTextPublicKey = curve.g.mul(cipherTextPrivateKey);
113
+ return {
114
+ ciphertext: encrypted,
115
+ secretKey: secretKey,
116
+ nonce: iv,
117
+ authTag: cipher.getAuthTag().toString("hex"),
118
+ cipherTextPublicKey: cipherTextPublicKey.encode('hex')
119
+ };
120
+ }
121
+
122
+ function decrypt(privateKeyFile, encryptedMsg) {
123
+ //console.log(privateKeyFile);
124
+ const privatePoint = getPrivatePointFromPrivateKey(privateKeyFile);
125
+ return decrypt_ecc(encryptedMsg, privatePoint);
126
+ }
127
+
128
+ function decrypt_ecc(encryptedMsg, privatePoint) {
129
+ try {
130
+ const {ciphertext, nonce, authTag, x, y} = encryptedMsg;
131
+ const publicPoint = getPublicPointFromCoords(`${x}`, `${y}`);
132
+ const sharedEccKey = publicPoint.mul(privatePoint);
133
+ const secretKey = ecc_point_to_256_bit_key(sharedEccKey);
134
+
135
+ const decipher = crypto.createDecipheriv('aes-256-gcm', secretKey, Buffer.from(nonce, 'hex'));
136
+ decipher.setAuthTag(Buffer.from(authTag, 'hex'));
137
+ let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
138
+ decrypted += decipher.final('utf8');
139
+ return decrypted;
140
+ } catch (e) {
141
+ console.log(`Error while decrypting ECC:`, e.message);
142
+ return false;
143
+ }
144
+ }
145
+
146
+ function hexToBytes(hexStr) {
147
+ if (hexStr.startsWith("0x")) {
148
+ hexStr = hexStr.slice(2);
149
+ }
150
+ return Buffer.from(hexStr, "hex");
151
+ }
152
+
153
+ function encryptWithPublicKey(publicKey, data) {
154
+ // Returned object contains 4 properties: version, ephemPublicKey, nonce, ciphertext
155
+ // Each contains data encoded using base64, version is always the same string
156
+ const enc = ethSigUtil.encrypt({
157
+ publicKey: hexToBytes(publicKey).toString('base64'),
158
+ data: ascii85.encode(Buffer.from(data)).toString(),
159
+ version: 'x25519-xsalsa20-poly1305',
160
+ });
161
+
162
+ // We want to store the data in smart contract, therefore we concatenate them
163
+ // into single Buffer
164
+ const buf = Buffer.concat([
165
+ Buffer.from(enc.ephemPublicKey, 'base64'),
166
+ Buffer.from(enc.nonce, 'base64'),
167
+ Buffer.from(enc.ciphertext, 'base64'),
168
+ ]);
169
+
170
+ // In smart contract we are using `bytes[112]` variable (fixed size byte array)
171
+ // you might need to use `bytes` type for dynamic sized array
172
+ // We are also using ethers.js which requires type `number[]` when passing data
173
+ // for argument of type `bytes` to the smart contract function
174
+ // Next line just converts the buffer to `number[]` required by contract function
175
+ // THIS LINE IS USED IN OUR ORIGINAL CODE:
176
+ // return buf.toJSON().data;
177
+
178
+ // Return just the Buffer to make the function directly compatible with decryptData function
179
+ return buf;
180
+ }
181
+
182
+ // (async () => {
183
+ // try {
184
+ // const message = 'testabc1';
185
+ // console.log('initial message:', message);
186
+ // const publicKeyPEM = getPEMFromPublic('./app/cert1-ca1-clean.crt');
187
+ // const ECKey = require('ec-key');
188
+ // const key = new ECKey(publicKeyPEM, 'pem');
189
+ // const encrypted_message = encrypt(key.x, key.y, message);
190
+ // console.log(encrypted_message);
191
+ // console.log(encryptedDataToBase64Json(encrypted_message));
192
+ // //
193
+ // // const encrypted_message = 'eyJjaXBoZXJ0ZXh0IjoiZTYzZTcyNDY0MWY4YWRmN2QyNjNmMTI0ZGYxZTdlYzZmNGM5NGU1NCIsIm5vbmNlIjoiYmE0YWU5YjMyOWFhZWRiM2ViZTM5MGU4YTY3YWFiNmMiLCJhdXRoVGFnIjoiOGU2NmI4NTVmNTE3N2E0NzZlMjkwNTE3Y2Q2ZDE3ZmUiLCJ4IjoiN2E1MjhhNGI5YzAzODM4Mjc0ZmFiYzVlM2RiOWQ2ZWFiNzBiZTFiMDA3YWJiOTcyYzI0YmFhNGE1ZmI4OGJjNTVmNDVlNGNkMjZjY2NhNTFhOTk3OGZkNDRhZmE1YjQ0IiwieSI6IjE5M2NjMWYwMzVmOGMxZjEzNGU4MTEzOGIyOTRiODBiZWQyYjkyMzdkZmIyOTMyOGRjOTg1MzlmMjhkNjIzMTJiYTQ2OTU2NDFkYjIyNjk0ZWUyNGFjZjQ4MjBiZDlhMSJ9';
194
+ // // const messageDecrypted = decrypt('./app/cert1-ca1-clean.key', encryptedDataToBase64Json(encrypted_message));
195
+ // // console.log('decrypted message:', messageDecrypted);
196
+ // } catch (e) {
197
+ // console.log(e);
198
+ // }
199
+ //
200
+ // })();
201
+
202
+ module.exports = {
203
+ encryptedDataFromBase64Json,
204
+ encryptedDataToBase64Json,
205
+ encryptWithPublicKey,
206
+ decrypt,
207
+ encrypt,
208
+ sha256,
209
+ fileChecksum,
210
+ fileChecksumFromSwiftStream
211
+ };
@@ -2,34 +2,84 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { TaskStatus } = require('./task_status');
4
4
 
5
- // Load all JavaScript files from the ./serverless/ folder
6
- // const serverlessDir = path.join(__dirname, './serverless');
7
-
8
- // let backendFunctions = {};
9
- // fs.readdirSync(serverlessDir).forEach(file => {
10
- // if (file.endsWith('.js')) {
11
- // const filePath = path.join(serverlessDir, file);
12
- // const moduleExports = require(filePath.replace('.js', ''));
13
- // backendFunctions = { ...backendFunctions, ...moduleExports };
14
- // }
15
- // });
16
- const { hello } = require('./serverless/backend');
5
+ // Load EVERY export of ./serverless/backend into the payload's scope. The
6
+ // backend is copied next to this file by ecld-build (and staged the same way
7
+ // by ecld-test locally), so one code path serves both the enclave and local
8
+ // testing. Previously only a function literally named `hello` was reachable
9
+ // (hardcoded destructure); any other exported function threw ReferenceError.
10
+ // A missing backend leaves only ___etny_result___ in scope, which is the
11
+ // stock-enclave behaviour.
12
+ let backendFunctions = {};
13
+ // If the backend fails to load, remember WHY. A silently-swallowed failure
14
+ // (missing npm module, bad require inside backend.js) makes every task die
15
+ // later with a misleading "X is not defined". Only a genuinely absent backend
16
+ // stays silent — that is the stock-enclave configuration.
17
+ let backendImportError = null;
18
+ try {
19
+ backendFunctions = { ...require('./serverless/backend') };
20
+ } catch (e) {
21
+ backendFunctions = {};
22
+ // MODULE_NOT_FOUND messages embed the whole require stack (which always
23
+ // mentions serverless/backend.js), so parse out WHICH module is missing:
24
+ // only a missing backend module itself means "no backend shipped".
25
+ const m = /Cannot find module '([^']+)'/.exec(e.message || '');
26
+ const missingBackendItself =
27
+ e.code === 'MODULE_NOT_FOUND' && m && /serverless[\\/]backend/.test(m[1]);
28
+ if (!missingBackendItself) {
29
+ backendImportError = `${e.name || 'Error'}: ${(e.message || '').split('\n')[0]}`;
30
+ }
31
+ }
17
32
 
18
33
  function ___etny_result___(data) {
19
34
  return [0, data];
20
35
  }
36
+
37
+ // Names of *_ADDRESS-style enclave config vars that are set but EMPTY.
38
+ // The enclave is sealed: env can only come from the image, so a present-but-
39
+ // empty required value is always a build/render defect, never a runtime
40
+ // choice. ESR_* vars only exist when the project enabled ESR at build time.
41
+ function emptyRequiredConfig() {
42
+ const required = ['ESR_CONTRACT_ADDRESS'];
43
+ return required.filter(
44
+ (name) => name in process.env && !String(process.env[name]).trim()
45
+ );
46
+ }
47
+
21
48
  function executeTask(payload, input) {
22
- return exec(payload, input, { '___etny_result___': ___etny_result___, 'hello': hello });
49
+ const missing = emptyRequiredConfig();
50
+ if (missing.length) {
51
+ return [
52
+ TaskStatus.CONFIG_ERROR,
53
+ 'ENCLAVE CONFIG ERROR: required value(s) empty inside the enclave: ' +
54
+ missing.join(', ') +
55
+ ' | The enclave is sealed, so this value had to be baked at build' +
56
+ ' time and was not. Re-run ecld-build (it validates ESR config)' +
57
+ ' and republish.'
58
+ ];
59
+ }
60
+ if (backendImportError !== null) {
61
+ return [
62
+ TaskStatus.IMPORT_ERROR,
63
+ 'BACKEND IMPORT ERROR: ' + backendImportError +
64
+ ' | The serverless backend failed to load inside the enclave, so none of its' +
65
+ ' functions are available. Common causes: a module missing from the enclave' +
66
+ ' image dependencies, or a bad require() inside backend.js.'
67
+ ];
68
+ }
69
+ return exec(payload, input, { '___etny_result___': ___etny_result___, ...backendFunctions });
23
70
  }
71
+
24
72
  function exec(payload, input, globals = null) {
25
73
  try {
26
74
  if (payload && payload !== "") {
75
+ const scope = globals || {};
27
76
  if (input && input !== "") {
28
- if (globals) {
29
- globals['___etny_data_set___'] = input;
30
- }
31
- return ___etny_result___(eval(payload));
32
- } else {
77
+ scope['___etny_data_set___'] = input;
78
+ }
79
+ // `with` exposes every backend function and ___etny_data_set___ to
80
+ // the payload by bare name — matching how payloads are written
81
+ // (e.g. `processData(___etny_data_set___)`).
82
+ with (scope) {
33
83
  return ___etny_result___(eval(payload));
34
84
  }
35
85
  } else {