Skip to content

Commit

Permalink
feat/safe: Prevent safe api filesys access in browser
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuef committed Mar 11, 2020
1 parent bbfe1ce commit 28a103e
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 5 deletions.
65 changes: 65 additions & 0 deletions app/extensions/safe/test/app/saferSafe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { SaferSafe } from '$App/extensions/safe/webviewProcess/saferSafe';

// jest.mock('safe-nodejs');

describe( 'Filesystem safe SAFE', () => {
let safe;
beforeEach( () => {
safe = new SaferSafe();
} );

test( 'a SAFE object has needed methods', async () => {
expect( typeof safe.files_container_create ).toBe( 'function' );
expect( typeof safe.files_container_add ).toBe( 'function' );
expect( typeof safe.files_container_sync ).toBe( 'function' );

// not strictly needed, but lets check it exists on our new class
expect( typeof safe.files_container_add_from_raw ).toBe( 'function' );
} );

test( 'attempting to use location fails', async () => {
expect( () => {
safe.files_container_create( 'some_location', '', true, true, true );
} ).toThrow( /"location"/ );
expect( () => {
safe.files_container_sync( 'some_location', '', true, true, true );
} ).toThrow( /"location"/ );
expect( () => {
safe.files_container_add( 'some_none_safe_location', '', true, true, true );
} ).toThrow( /"location".+safe:/ );
} );

test( 'attempting to use safe container add with a safe: url does not fail', async () => {
// it will still fail as args not correct and we're not mocking safe here.
expect( () => {
safe.files_container_add(
'safe://some_safe_location',
'',
true,
true,
true
);
} ).not.toThrow( /"location".+safe:/ );
} );

test( 'attempting to use safe container create should fail when a string is passed', async () => {
expect( () => {
safe.files_container_create( 's', '', true, true, true );
} ).toThrow( /File object/ );
} );

test( 'attempting to use safe container create with locaton object should fail', async () => {
const x = {};

expect( () => {
safe.files_container_create( x, '', true, true, true );
} ).toThrow( /File object/ );
} );

test( 'attempting to use safe container create with File object should throw temp error', async () => {
const x = new File( [], 'test' );
expect( () => {
safe.files_container_create( x, '', true, true, true );
} ).toThrow( /location" argument cannot be used/ );
} );
} );
94 changes: 94 additions & 0 deletions app/extensions/safe/webviewProcess/saferSafe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Safe } from 'safe-nodejs';

/**
* Override the nodejs filesystem API to prevent file system access byt apps, and require them to use the browser's File APIs.
*/
export class SaferSafe extends Safe {
files_container_create = (
location?: any,
destination?: string,
recursive: boolean,
del: boolean,
updateNrs: boolean,
dryRun: boolean
): void => {
if ( location && !( location instanceof File ) ) {
throw new Error( `
"location", if passed, must be a File object.
` );
}

if ( location ) {
// Please use "Safe.files_container_create_from_raw" and the native browser "FileReader" API: https://developer.mozilla.org/en-US/docs/Web/API/FileReader
throw new Error( `
The "location" argument cannot be used on "files_container_create" in the browser yet.
` );
}

return Reflect.apply( Safe.prototype.files_container_create, this, [
null,
null,
del,
updateNrs,
dryRun
] );
};

files_container_sync = (
location?: any,
destination?: string,
recursive: boolean,
del: boolean,
updateNrs: boolean,
dryRun: boolean
): void => {
if ( location && !( location instanceof File ) ) {
throw new Error( `
"location", if passed, must be a File object.
` );
}

if ( location ) {
// TODO: enable Files object use
throw new Error( `
The "location" argument cannot be used on "files_container_sync" in the browser yet.
` );
}

return Reflect.apply( Safe.prototype.files_container_sync, this, [
null,
null,
del,
updateNrs,
dryRun
] );
};

files_container_add = (
location: string,
destination: string,
force: boolean,
updateNrs: boolean,
dryRun: boolean
): void => {
if ( typeof location !== 'string' ) {
throw new Error( `
The "location" must be a "safe:" url string.
` );
}

if ( !location.startsWith( 'safe:' ) ) {
throw new Error( `
"location" must start with "safe:"
` );
}

return Reflect.apply( Safe.prototype.files_container_add, this, [
location,
destination,
force,
updateNrs,
dryRun
] );
};
}
10 changes: 5 additions & 5 deletions app/extensions/safe/webviewProcess/webviewPreload.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import EventEmitter from 'events';
import { Safe, XorUrlEncoder } from 'safe-nodejs';
import { XorUrlEncoder } from 'safe-nodejs';

import { SaferSafe } from '$Extensions/safe/webviewProcess/saferSafe';
// eslint-disable-next-line import/extensions
import pkg from '$Package';
import { logger } from '$Logger';
// import * as remoteCallActions from '$Actions/remoteCall_actions';
import { PROTOCOLS, CONFIG, isRunningTestCafeProcess } from '$Constants';

// shim for rdflib.js
// eslint-disable-next-line no-underscore-dangle
Expand Down Expand Up @@ -103,10 +102,11 @@ export const setupWebIdEventEmitter = ( passedStore, win = window ) => {

export const setupSafeAPIs = ( passedStore, win = window ) => {
const theWindow = win;
logger.info( 'Setup up SAFE Dom API...' );
logger.info( 'Setup up SAFE Dom API... UPDATED' );

// use from passed object if present (for testing)
theWindow.Safe = theWindow.Safe || Safe;
theWindow.Safe = theWindow.Safe || SaferSafe;

theWindow.XorUrlEncoder = theWindow.XorUrlEncoder || XorUrlEncoder;
};

Expand Down

0 comments on commit 28a103e

Please sign in to comment.