Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@appium/base-driver

Package Overview
Dependencies
Maintainers
7
Versions
85
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@appium/base-driver - npm Package Compare versions

Comparing version 9.3.16 to 9.3.17

3

build/lib/basedriver/core.js

@@ -214,4 +214,3 @@ "use strict";

`the security ramifications, please do so by following ` +
`the documented instructions at https://github.com/appium` +
`/appium/blob/master/docs/en/writing-running-appium/security.md`);
`the documented instructions at http://appium.io/docs/en/2.0/guides/security/`);
}

@@ -218,0 +217,0 @@ }

@@ -72,3 +72,3 @@ "use strict";

async function calculateFolderIntegrity(folderPath) {
return (await support_1.fs.glob('**/*', { cwd: folderPath, strict: false, nosort: true })).length;
return (await support_1.fs.glob('**/*', { cwd: folderPath })).length;
}

@@ -439,3 +439,2 @@ async function calculateFileIntegrity(filePath) {

cwd: tmpRoot,
strict: false,
// Get the top level match

@@ -442,0 +441,0 @@ })).sort((a, b) => a.split(path_1.default.sep).length - b.split(path_1.default.sep).length);

@@ -1,2 +0,6 @@

export function handleIdempotency(req: any, res: any, next: any): Promise<any>;
/**
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
export function handleIdempotency(req: import('express').Request, res: import('express').Response, next: any): Promise<any>;
//# sourceMappingURL=idempotency.d.ts.map

@@ -9,29 +9,25 @@ "use strict";

const lru_cache_1 = __importDefault(require("lru-cache"));
const support_1 = require("@appium/support");
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const lodash_1 = __importDefault(require("lodash"));
const events_1 = require("events");
const CACHE_SIZE = 1024;
const IDEMPOTENT_RESPONSES = new lru_cache_1.default({
max: CACHE_SIZE,
max: 64,
ttl: 30 * 60 * 1000,
updateAgeOnGet: true,
dispose(key, { response }) {
if (response) {
support_1.fs.rimrafSync(response);
}
},
updateAgeOnHas: true,
dispose: (key, { responseStateListener }) => {
responseStateListener.removeAllListeners();
}
});
const MONITORED_METHODS = ['POST', 'PATCH'];
const IDEMPOTENCY_KEY_HEADER = 'x-idempotency-key';
process.on('exit', () => {
const resPaths = [...IDEMPOTENT_RESPONSES.values()].map(({ response }) => response).filter(Boolean);
for (const resPath of resPaths) {
try {
// Asynchronous calls are not supported in onExit handler
support_1.fs.rimrafSync(resPath);
}
catch (ign) { }
/**
*
* @param {string} key
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
function cacheResponse(key, req, res) {
if (!res.socket) {
return;
}
});
function cacheResponse(key, req, res) {
const responseStateListener = new events_1.EventEmitter();

@@ -44,29 +40,20 @@ IDEMPOTENT_RESPONSES.set(key, {

});
const tmpFile = path_1.default.resolve(os_1.default.tmpdir(), `${support_1.util.uuidV4()}.response`);
const responseListener = support_1.fs.createWriteStream(tmpFile, {
emitClose: true,
});
const originalSocketWriter = res.socket.write.bind(res.socket);
let response = '';
const patchedWriter = (chunk, encoding, next) => {
if (responseListener.writable) {
responseListener.write(chunk);
if (lodash_1.default.isString(chunk)) {
response += chunk;
}
else if (lodash_1.default.isFunction(chunk.toString)) {
response += chunk.toString(lodash_1.default.isString(encoding) ? encoding : undefined);
}
return originalSocketWriter(chunk, encoding, next);
};
res.socket.write = patchedWriter;
let writeError = null;
let httpErrorMessage = null;
let isResponseFullySent = false;
responseListener.once('error', (e) => {
writeError = e;
});
res.once('finish', () => {
isResponseFullySent = true;
responseListener.end();
});
res.once('error', (e) => { httpErrorMessage = e.message; });
req.once('error', (e) => { httpErrorMessage = e.message; });
res.once('finish', () => { isResponseFullySent = true; });
res.once('close', () => {
if (!isResponseFullySent) {
responseListener.end();
}
});
responseListener.once('close', () => {
if (res.socket?.write === patchedWriter) {

@@ -78,10 +65,8 @@ res.socket.write = originalSocketWriter;

`Cache consistency has been damaged`);
return responseStateListener.emit('ready', null);
}
if (writeError) {
logger_1.default.info(`Could not cache the response identified by '${key}': ${writeError.message}`);
else if (httpErrorMessage) {
logger_1.default.info(`Could not cache the response identified by '${key}': ${httpErrorMessage}`);
IDEMPOTENT_RESPONSES.delete(key);
return responseStateListener.emit('ready', null);
}
if (!isResponseFullySent) {
else if (!isResponseFullySent) {
logger_1.default.info(`Could not cache the response identified by '${key}', ` +

@@ -91,13 +76,23 @@ `because it has not been completed`);

IDEMPOTENT_RESPONSES.delete(key);
return responseStateListener.emit('ready', null);
}
IDEMPOTENT_RESPONSES.get(key).response = tmpFile;
responseStateListener.emit('ready', tmpFile);
const value = IDEMPOTENT_RESPONSES.get(key);
if (value) {
value.response = Buffer.from(response, 'utf8');
responseStateListener.emit('ready', value.response);
}
else {
responseStateListener.emit('ready', null);
}
});
}
/**
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async function handleIdempotency(req, res, next) {
const key = req.headers[IDEMPOTENCY_KEY_HEADER];
if (!key) {
const keyOrArr = req.headers[IDEMPOTENCY_KEY_HEADER];
if (!keyOrArr) {
return next();
}
const key = lodash_1.default.isArray(keyOrArr) ? keyOrArr[0] : keyOrArr;
if (!MONITORED_METHODS.includes(req.method)) {

@@ -113,4 +108,4 @@ // GET, DELETE, etc. requests are idempotent by default

}
const { method: storedMethod, path: storedPath, response, responseStateListener, } = IDEMPOTENT_RESPONSES.get(key);
if (req.method !== storedMethod || req.path !== storedPath) {
const { method, path, response, responseStateListener, } = IDEMPOTENT_RESPONSES.get(key);
if (req.method !== method || req.path !== path) {
logger_1.default.warn(`Got two different requests with the same idempotency key '${key}'`);

@@ -120,15 +115,9 @@ logger_1.default.warn('Is the client generating idempotency keys properly?');

}
const rerouteCachedResponse = async (cachedResPath) => {
if (!(await support_1.fs.exists(cachedResPath))) {
IDEMPOTENT_RESPONSES.delete(key);
logger_1.default.warn(`Could not read the cached response identified by key '${key}'`);
logger_1.default.warn('The temporary storage is not accessible anymore');
return next();
}
support_1.fs.createReadStream(cachedResPath).pipe(res.socket);
};
if (response) {
logger_1.default.info(`The same request with the idempotency key '${key}' has been already processed`);
logger_1.default.info(`Rerouting its response to the current request`);
await rerouteCachedResponse(response);
if (!res.socket?.writable) {
return next();
}
res.socket.write(response.toString('utf8'));
}

@@ -138,7 +127,7 @@ else {

logger_1.default.info(`Waiting for the response to be rerouted to the current request`);
responseStateListener.once('ready', async (cachedResponsePath) => {
if (!cachedResponsePath) {
responseStateListener.once('ready', async (/** @type {Buffer?} */ cachedResponse) => {
if (!cachedResponse || !res.socket?.writable) {
return next();
}
await rerouteCachedResponse(cachedResponsePath);
res.socket.write(cachedResponse.toString('utf8'));
});

@@ -145,0 +134,0 @@ }

@@ -90,2 +90,3 @@ "use strict";

}
// @ts-ignore
app.use(middleware_1.handleIdempotency);

@@ -92,0 +93,0 @@ app.use((0, middleware_1.fixPythonContentType)(basePath));

@@ -296,4 +296,3 @@ /* eslint-disable no-unused-vars */

`the security ramifications, please do so by following ` +
`the documented instructions at https://github.com/appium` +
`/appium/blob/master/docs/en/writing-running-appium/security.md`
`the documented instructions at http://appium.io/docs/en/2.0/guides/security/`
);

@@ -300,0 +299,0 @@ }

@@ -75,3 +75,3 @@ import _ from 'lodash';

async function calculateFolderIntegrity(folderPath) {
return (await fs.glob('**/*', {cwd: folderPath, strict: false, nosort: true})).length;
return (await fs.glob('**/*', {cwd: folderPath})).length;
}

@@ -479,3 +479,2 @@

cwd: tmpRoot,
strict: false,
// Get the top level match

@@ -482,0 +481,0 @@ })

import log from './logger';
import LRU from 'lru-cache';
import {fs, util} from '@appium/support';
import os from 'os';
import path from 'path';
import _ from 'lodash';
import {EventEmitter} from 'events';
const CACHE_SIZE = 1024;
const IDEMPOTENT_RESPONSES = new LRU({
max: CACHE_SIZE,
max: 64,
ttl: 30 * 60 * 1000,
updateAgeOnGet: true,
dispose(key, {response}) {
if (response) {
fs.rimrafSync(response);
}
},
updateAgeOnHas: true,
dispose: (key, {responseStateListener}) => {
responseStateListener.removeAllListeners();
}
});

@@ -21,13 +18,13 @@ const MONITORED_METHODS = ['POST', 'PATCH'];

process.on('exit', () => {
const resPaths = [...IDEMPOTENT_RESPONSES.values()].map(({response}) => response).filter(Boolean);
for (const resPath of resPaths) {
try {
// Asynchronous calls are not supported in onExit handler
fs.rimrafSync(resPath);
} catch (ign) {}
/**
*
* @param {string} key
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
function cacheResponse(key, req, res) {
if (!res.socket) {
return;
}
});
function cacheResponse(key, req, res) {
const responseStateListener = new EventEmitter();

@@ -40,10 +37,9 @@ IDEMPOTENT_RESPONSES.set(key, {

});
const tmpFile = path.resolve(os.tmpdir(), `${util.uuidV4()}.response`);
const responseListener = fs.createWriteStream(tmpFile, {
emitClose: true,
});
const originalSocketWriter = res.socket.write.bind(res.socket);
let response = '';
const patchedWriter = (chunk, encoding, next) => {
if (responseListener.writable) {
responseListener.write(chunk);
if (_.isString(chunk)) {
response += chunk;
} else if (_.isFunction(chunk.toString)) {
response += chunk.toString(_.isString(encoding) ? encoding : undefined);
}

@@ -53,17 +49,8 @@ return originalSocketWriter(chunk, encoding, next);

res.socket.write = patchedWriter;
let writeError = null;
let httpErrorMessage = null;
let isResponseFullySent = false;
responseListener.once('error', (e) => {
writeError = e;
});
res.once('finish', () => {
isResponseFullySent = true;
responseListener.end();
});
res.once('error', (e) => { httpErrorMessage = e.message; });
req.once('error', (e) => { httpErrorMessage = e.message; });
res.once('finish', () => { isResponseFullySent = true; });
res.once('close', () => {
if (!isResponseFullySent) {
responseListener.end();
}
});
responseListener.once('close', () => {
if (res.socket?.write === patchedWriter) {

@@ -76,31 +63,37 @@ res.socket.write = originalSocketWriter;

`Could not cache the response identified by '${key}'. ` +
`Cache consistency has been damaged`
`Cache consistency has been damaged`
);
return responseStateListener.emit('ready', null);
}
if (writeError) {
log.info(`Could not cache the response identified by '${key}': ${writeError.message}`);
} else if (httpErrorMessage) {
log.info(`Could not cache the response identified by '${key}': ${httpErrorMessage}`);
IDEMPOTENT_RESPONSES.delete(key);
return responseStateListener.emit('ready', null);
}
if (!isResponseFullySent) {
} else if (!isResponseFullySent) {
log.info(
`Could not cache the response identified by '${key}', ` +
`because it has not been completed`
`because it has not been completed`
);
log.info('Does the client terminate connections too early?');
IDEMPOTENT_RESPONSES.delete(key);
return responseStateListener.emit('ready', null);
}
IDEMPOTENT_RESPONSES.get(key).response = tmpFile;
responseStateListener.emit('ready', tmpFile);
const value = IDEMPOTENT_RESPONSES.get(key);
if (value) {
value.response = Buffer.from(response, 'utf8');
responseStateListener.emit('ready', value.response);
} else {
responseStateListener.emit('ready', null);
}
});
}
/**
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async function handleIdempotency(req, res, next) {
const key = req.headers[IDEMPOTENCY_KEY_HEADER];
if (!key) {
const keyOrArr = req.headers[IDEMPOTENCY_KEY_HEADER];
if (!keyOrArr) {
return next();
}
const key = _.isArray(keyOrArr) ? keyOrArr[0] : keyOrArr;
if (!MONITORED_METHODS.includes(req.method)) {

@@ -119,8 +112,8 @@ // GET, DELETE, etc. requests are idempotent by default

const {
method: storedMethod,
path: storedPath,
method,
path,
response,
responseStateListener,
} = IDEMPOTENT_RESPONSES.get(key);
if (req.method !== storedMethod || req.path !== storedPath) {
if (req.method !== method || req.path !== path) {
log.warn(`Got two different requests with the same idempotency key '${key}'`);

@@ -131,24 +124,17 @@ log.warn('Is the client generating idempotency keys properly?');

const rerouteCachedResponse = async (cachedResPath) => {
if (!(await fs.exists(cachedResPath))) {
IDEMPOTENT_RESPONSES.delete(key);
log.warn(`Could not read the cached response identified by key '${key}'`);
log.warn('The temporary storage is not accessible anymore');
return next();
}
fs.createReadStream(cachedResPath).pipe(res.socket);
};
if (response) {
log.info(`The same request with the idempotency key '${key}' has been already processed`);
log.info(`Rerouting its response to the current request`);
await rerouteCachedResponse(response);
if (!res.socket?.writable) {
return next();
}
res.socket.write(response.toString('utf8'));
} else {
log.info(`The same request with the idempotency key '${key}' is being processed`);
log.info(`Waiting for the response to be rerouted to the current request`);
responseStateListener.once('ready', async (cachedResponsePath) => {
if (!cachedResponsePath) {
responseStateListener.once('ready', async (/** @type {Buffer?} */ cachedResponse) => {
if (!cachedResponse || !res.socket?.writable) {
return next();
}
await rerouteCachedResponse(cachedResponsePath);
res.socket.write(cachedResponse.toString('utf8'));
});

@@ -155,0 +141,0 @@ }

@@ -121,2 +121,3 @@ import _ from 'lodash';

}
// @ts-ignore
app.use(handleIdempotency);

@@ -123,0 +124,0 @@ app.use(fixPythonContentType(basePath));

{
"name": "@appium/base-driver",
"version": "9.3.16",
"version": "9.3.17",
"description": "Base driver class for Appium drivers",

@@ -47,9 +47,9 @@ "keywords": [

"dependencies": {
"@appium/support": "^4.1.3",
"@appium/types": "^0.13.2",
"@colors/colors": "1.5.0",
"@appium/support": "^4.1.4",
"@appium/types": "^0.13.3",
"@colors/colors": "1.6.0",
"@types/async-lock": "1.4.0",
"@types/bluebird": "3.5.38",
"@types/express": "4.17.17",
"@types/lodash": "4.14.195",
"@types/lodash": "4.14.196",
"@types/method-override": "0.0.32",

@@ -71,3 +71,3 @@ "@types/serve-favicon": "2.5.4",

"source-map-support": "0.5.21",
"type-fest": "3.11.1",
"type-fest": "3.13.1",
"validate.js": "0.13.1"

@@ -82,3 +82,3 @@ },

},
"gitHead": "ec57ff4c2a1ae3ecbab11bd02d4249ca67626d83",
"gitHead": "d6204b6902074210943d7bbbf72d139b9b170a20",
"typedoc": {

@@ -85,0 +85,0 @@ "entryPoint": "./lib/index.js"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc