Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows support #19

Merged
merged 7 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
# The default folder to put you mitmproxy addons to import
mitmproxy-addons/har_dump.py
.venv/

tmp/
*.tmp
tmp.*
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ The current features include:
- Collecting HTTP(S) traffic in HAR format
- Automatic CA management and WireGuard mitmproxy setup

**Currently, this module only supports POSIX environments. It will not run on Windows out of the box.**

## Installation

You can install cyanoacrylate using yarn or npm:
Expand Down Expand Up @@ -42,7 +40,7 @@ The following example collects the traffic for an app in the Android emulator. I

```ts
(async () => {
const analysis = startAnalysis({
const analysis = await startAnalysis({
platform: 'android',
runTarget: 'emulator',
capabilities: [],
Expand Down
42 changes: 21 additions & 21 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Functions that can be used to instrument the device and analyze apps.

#### Defined in

[src/index.ts:42](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L42)
[cyanoacrylate/src/index.ts:41](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L41)

___

Expand All @@ -83,7 +83,7 @@ The options for the `startAnalysis()` function.

#### Defined in

[src/index.ts:252](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L252)
[cyanoacrylate/src/index.ts:251](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L251)

___

Expand All @@ -95,7 +95,7 @@ An ID of a known permission on Android.

#### Defined in

node_modules/appstraction/dist/index.d.ts:35
appstraction/dist/index.d.ts:36

___

Expand All @@ -114,7 +114,7 @@ Metadata about an app.

#### Defined in

[src/index.ts:26](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L26)
[cyanoacrylate/src/index.ts:25](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L25)

___

Expand Down Expand Up @@ -148,7 +148,7 @@ Functions that can be used to control an app analysis.

#### Defined in

[src/index.ts:115](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L115)
[cyanoacrylate/src/index.ts:114](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L114)

___

Expand All @@ -167,7 +167,7 @@ The result of an app analysis.

#### Defined in

[src/index.ts:201](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L201)
[cyanoacrylate/src/index.ts:200](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L200)

___

Expand All @@ -185,7 +185,7 @@ A supported attribute for the `getDeviceAttribute()` function, depending on the

#### Defined in

node_modules/appstraction/dist/index.d.ts:302
appstraction/dist/index.d.ts:305

___

Expand All @@ -204,7 +204,7 @@ The options for each attribute available through the `getDeviceAttribute()` func

#### Defined in

node_modules/appstraction/dist/index.d.ts:304
appstraction/dist/index.d.ts:307

___

Expand All @@ -216,7 +216,7 @@ An ID of a known permission on iOS.

#### Defined in

node_modules/appstraction/dist/index.d.ts:39
appstraction/dist/index.d.ts:40

___

Expand Down Expand Up @@ -260,7 +260,7 @@ Functions that are available for the platforms.

#### Defined in

node_modules/appstraction/dist/index.d.ts:45
appstraction/dist/index.d.ts:46

___

Expand Down Expand Up @@ -292,7 +292,7 @@ The options for a specific platform/run target combination.

#### Defined in

[src/index.ts:213](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L213)
[cyanoacrylate/src/index.ts:212](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L212)

___

Expand All @@ -310,7 +310,7 @@ A capability supported by this library.

#### Defined in

[src/index.ts:19](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L19)
[cyanoacrylate/src/index.ts:18](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L18)

___

Expand All @@ -322,7 +322,7 @@ A platform that is supported by this library.

#### Defined in

node_modules/appstraction/dist/index.d.ts:41
appstraction/dist/index.d.ts:42

___

Expand All @@ -340,7 +340,7 @@ A run target that is supported by this library for the given platform.

#### Defined in

node_modules/appstraction/dist/index.d.ts:43
appstraction/dist/index.d.ts:44

___

Expand All @@ -356,7 +356,7 @@ Options for a traffic collection that specifies which apps to collect traffic fr

#### Defined in

[src/index.ts:40](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L40)
[cyanoacrylate/src/index.ts:39](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L39)

## Variables

Expand All @@ -368,7 +368,7 @@ The IDs of known permissions on Android.

#### Defined in

node_modules/appstraction/dist/index.d.ts:33
appstraction/dist/index.d.ts:34

___

Expand All @@ -380,7 +380,7 @@ The IDs of known permissions on iOS.

#### Defined in

node_modules/appstraction/dist/index.d.ts:37
appstraction/dist/index.d.ts:38

## Functions

Expand All @@ -402,13 +402,13 @@ Pause for a given duration.

#### Defined in

node_modules/appstraction/dist/index.d.ts:8
appstraction/dist/index.d.ts:9

___

### startAnalysis

▸ **startAnalysis**<`Platform`, `RunTarget`, `Capabilities`\>(`analysisOptions`): [`Analysis`](README.md#analysis)<`Platform`, `RunTarget`, `Capabilities`\>
▸ **startAnalysis**<`Platform`, `RunTarget`, `Capabilities`\>(`analysisOptions`): `Promise`<[`Analysis`](README.md#analysis)<`Platform`, `RunTarget`, `Capabilities`\>\>

Initialize an analysis for the given platform and run target. Remember to call `stop()` on the returned object when
you want to end the analysis.
Expand All @@ -429,10 +429,10 @@ you want to end the analysis.

#### Returns

[`Analysis`](README.md#analysis)<`Platform`, `RunTarget`, `Capabilities`\>
`Promise`<[`Analysis`](README.md#analysis)<`Platform`, `RunTarget`, `Capabilities`\>\>

An object that can be used to instrument the device and analyze apps.

#### Defined in

[src/index.ts:286](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L286)
[cyanoacrylate/src/index.ts:285](https://github.com/tweaselORG/cyanoacrylate/blob/main/src/index.ts#L285)
2 changes: 1 addition & 1 deletion examples/multiple-apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { pause, startAnalysis } from '../src/index';
const snapshotName = process.argv[3] || 'snapshot-with-setup-emu';
const apkFolder = process.argv[4] || 'path/to/app-files';

const analysis = startAnalysis({
const analysis = await startAnalysis({
platform: 'android',
runTarget: 'emulator',
capabilities: ['frida', 'certificate-pinning-bypass'],
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"types": "dist/index.d.ts",
"files": [
"/dist",
"/mitmproxy-addons/ipcEventsAddon.py",
"requirements.txt",
"/scripts"
"/scripts",
"/src/ipcEventsAddon.py"
],
"scripts": {
"build": "parcel build",
Expand All @@ -55,10 +55,11 @@
"prettier": "@baltpeter/prettier-config",
"dependencies": {
"@types/har-format": "^1.2.10",
"appstraction": "^0.1.2",
"cross-dirname": "^0.1.0",
"appstraction": "^0.2.0",
"cross-fetch": "^3.1.5",
"ctrlc-windows": "^2.1.0",
"execa": "^7.0.0",
"global-cache-dir": "^4.4.0",
"js-ini": "^1.6.0",
"p-timeout": "^6.1.1",
"tempy": "^3.0.0"
Expand Down
85 changes: 85 additions & 0 deletions scripts/common/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck

import { Buffer } from 'buffer';
import fetch from 'cross-fetch';
import { execa } from 'execa';
import { existsSync } from 'fs';
import { copyFile, mkdir, writeFile } from 'fs/promises';
import globalCacheDir from 'global-cache-dir';
import { homedir } from 'os';
import { join } from 'path';

// Set up our Python dependencies (a venv with the modules from `requirements.txt` and the mitmproxy addons).
// This is meant to be run in the `postinstall` script. It will always install the full set of dependencies, regardless
// of whether they are already installed.
export const setupPythonDependencies = async () => {
const cacheDir = await globalCacheDir('cyanoacrylate');

const venvDir = join(cacheDir, 'venv');
const mitmproxyAddonsDir = join(cacheDir, 'mitmproxy-addons');
await mkdir(mitmproxyAddonsDir, { recursive: true });

// eslint-disable-next-line no-undef
const pipBinary = process.platform === 'win32' ? join(venvDir, 'Scripts/pip.exe') : join(venvDir, 'bin/pip');
const mitmdumpBinary =
// eslint-disable-next-line no-undef
process.platform === 'win32' ? join(venvDir, 'Scripts/mitmdump.exe') : join(venvDir, 'bin/mitmdump');

// Create a venv and install all python requirements
await execa('python', ['-m', 'venv', venvDir], { stdio: 'inherit' });
await execa(pipBinary, ['install', '-r', 'requirements.txt'], { stdio: 'inherit' });

// Download the har_dump.py addon corresponding to the current mitmproxy version
const mitmproxyVersion = await execa(mitmdumpBinary, ['--version']).then(
({ stdout }) => stdout.match(/Mitmproxy: ([0-9.]+)/)?.[1]
);
const mitmproxyCommitSha = await fetch(
`https://api.github.com/repos/mitmproxy/mitmproxy/git/ref/tags/${mitmproxyVersion}`
)
.then((res) => res.json())
.then((ref) => ref.object.sha);
await fetch(
`https://raw.githubusercontent.com/mitmproxy/mitmproxy/${mitmproxyCommitSha}/examples/contrib/har_dump.py`
)
.then((res) => res.arrayBuffer())
.then((hardumpScript) => writeFile(join(mitmproxyAddonsDir, 'har_dump.py'), Buffer.from(hardumpScript)));

// Copy the ipcEventsAddon.py addon to the mitmproxy addons directory
await copyFile(
// eslint-disable-next-line no-undef
new URL('../../src/ipcEventsAddon.py', import.meta.url),
join(mitmproxyAddonsDir, 'ipcEventsAddon.py')
);

// Start mitmproxy once to create certificate files if they don't exist, yet.
if (!existsSync(join(homedir(), '.mitmproxy'))) {
const mitmproxyProcess = execa(mitmdumpBinary, [
'-q',
'-s',
join(mitmproxyAddonsDir, 'ipcEventsAddon.py'),
'--set',
'ipcPipeFd=1',
]);
mitmproxyProcess.stdout?.addListener('data', (data) => {
const msg = JSON.parse(data);
if (msg.status === 'running') mitmproxyProcess.kill();
});
}
};

// This is a lighter version of `setupPythonDependencies`. It only installs if certain key files are missing.
// This isn't an exhaustive check but many orders of magnitude faster in the regular case (i.e. the dependencies are
// already installed). It is meant to always be run before an analysis is started.
export const ensurePythonDependencies = async () => {
const pathsThatNeedToExist = [
// The one file that exists both in Windows and *nix venvs.
'venv/pyvenv.cfg',
'mitmproxy-addons/har_dump.py',
'mitmproxy-addons/ipcEventsAddon.py',
];

const cacheDir = await globalCacheDir('cyanoacrylate');

if (pathsThatNeedToExist.some((path) => !existsSync(join(cacheDir, path)))) await setupPythonDependencies();
};
46 changes: 2 additions & 44 deletions scripts/postinstall.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,5 @@
import { Buffer } from 'buffer';
import fetch from 'cross-fetch';
import { execa } from 'execa';
import { existsSync } from 'fs';
import { writeFile } from 'fs/promises';
import { homedir } from 'os';
import { join } from 'path';
import { setupPythonDependencies } from './common/setup.js';

(async () => {
// Lifecycle scripts are always run from the project root, so this is always the same path (https://docs.npmjs.com/cli/v9/using-npm/scripts#best-practices).
const venvRoot = '.venv';

// Create a venv and install all python requirements
await execa('python', ['-m', 'venv', venvRoot], { stdio: 'inherit' });
await execa(`${venvRoot}/bin/pip`, ['install', '-r', 'requirements.txt'], {
stdio: 'inherit',
});

// Download the har_dump.py addon corresponding to the current mitmproxy version
const mitmproxyVersion = await execa(`${venvRoot}/bin/mitmdump`, ['--version']).then(
({ stdout }) => stdout.match(/Mitmproxy: ([0-9.]+)/)[1]
);
const mitmproxyCommitSha = await fetch(
`https://api.github.com/repos/mitmproxy/mitmproxy/git/ref/tags/${mitmproxyVersion}`
)
.then((res) => res.json())
.then((ref) => ref.object.sha);
await fetch(
`https://raw.githubusercontent.com/mitmproxy/mitmproxy/${mitmproxyCommitSha}/examples/contrib/har_dump.py`
)
.then((res) => res.arrayBuffer())
.then((hardumpScript) => writeFile('mitmproxy-addons/har_dump.py', Buffer.from(hardumpScript)));

// Start mitmproxy once to create certificate files if they don't exist, yet.
if (!existsSync(join(homedir(), '.mitmproxy'))) {
const mitmproxyProcess = execa(
`${venvRoot}/bin/mitmdump`,
['-s', 'mitmproxy-addons/ipcEventsAddon.py', '--set', 'ipcPipeFd=3'],
{
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
}
);
mitmproxyProcess.on('message', (msg) => {
if (msg.status === 'running') mitmproxyProcess.kill();
});
}
await setupPythonDependencies();
})();
Loading