Skip to content

Commit

Permalink
Merge pull request #200 from schemar/firewall
Browse files Browse the repository at this point in the history
Add FirewalledRule
  • Loading branch information
Pro authored May 2, 2019
2 parents 800b0d2 + 8c132ec commit 42bc1d4
Show file tree
Hide file tree
Showing 13 changed files with 437 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ build/
.idea/
.vscode/
*.iml
tags
*.swp

# LaTeX auxiliary files
*.aux
Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ before_install:
- sudo apt-get install software-properties-common
- sudo add-apt-repository -y ppa:ethereum/ethereum
install:
- docker pull trailofbits/eth-security-toolbox
- npm run update
- export PATH=./node_modules/.bin/:${PATH}
before_script:
Expand Down
105 changes: 105 additions & 0 deletions contracts/rules/FirewalledRule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
pragma solidity ^0.5.0;

// Copyright 2019 OpenST Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import "../organization/OrganizationInterface.sol";
import "../organization/Organized.sol";

/**
* @title Extend this rule in order to restrict relayers of a meta-transaction.
*
* @notice If you want to restrict the relayers that can relay meta-transactions
* to your rule, you can extend this contract with your rule. Your rule
* has to include the modifier `firewalled` for all functions that
* should be restricted.
* The firewall can be disabled, but not enabled again. When it is
* disabled, the modifier basically does nothing anymore.
*/
contract FirewalledRule is Organized {

/* Events */

/**
* Emitted when the firewall gets disabled.
* It is the only event, because a firewall cannot get re-enabled.
*/
event FirewallDisabled();


/* Storage */

/**
* Records whether the firewall is checked in the relevant modifier.
* Is set to `false` when the firewall gets disabled.
*/
bool public firewallEnabled;


/* Modifiers */

/**
* Requires that `tx.origin` is an organization worker, if the firewall is
* enabled. Once the firewall has been disabled, this modifier will not
* enforce any checks anymore.
* The modifier checks `tx.origin` and not `msg.sender`, because
* `msg.sender` can be another contract. However, what the firewall is
* supposed to check is who relayed the meta-transaction. The relayer is
* always `tx.origin`.
*/
modifier firewalled() {
if (firewallEnabled) {
require(
// We deliberately chose `tx.origin` and not `msg.sender`.
// See modifier documentation for more details.
// solium-disable-next-line security/no-tx-origin
organization.isWorker(tx.origin),
"This method is firewalled. Transaction must originate from an organization worker."
);
}

_;
}


/* Special Functions */

/**
* @param _organization The address of an organization contract.
*/
constructor(
OrganizationInterface _organization
)
Organized(_organization)
public
{
// The firewall must be enabled by default, as there is no function to
// enable a disabled firewall.
firewallEnabled = true;
}


/* External Functions */

/**
* @notice Disables the firewall so that anyone can use firewalled methods.
* Disabling the firewall cannot be done undone. There is no method
* to enable the firewall again.
* Emits a `FirewallDisabled` event.
*/
function disableFirewall() external onlyOrganization {
firewallEnabled = false;
emit FirewallDisabled();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pragma solidity ^0.5.0;

// Copyright 2019 OpenST Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import "./FirewalledRuleImplementation.sol";

/**
* @title A contract that calls a method that is firewalled.
*
* @notice The firewalled method explicitly checks for `tx.origin` as opposed to
* `msg.sender`. In order to test the desired behavior, an indirection
* is required. This contract provides exactly that indirection. It
* calls a firewalled method to assert that the firewall works on
* `tx.origin` and not `msg.sender`, as `msg.sender` is this contract.
*/
contract FirewalledRuleCaller {

/* Storage */

FirewalledRuleImplementation rule;


/* Special Functions */

constructor(FirewalledRuleImplementation _rule) public {
rule = _rule;
}


/* External Functions */

function callFirewalledFn() external view {
rule.firewalledFn();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
pragma solidity ^0.5.0;

// Copyright 2019 OpenST Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import "../../../rules/FirewalledRule.sol";

/**
* @title Implementation that actually has a firewalled method.
*
* @notice As the firewall itself doesn't have any firewalled methods, another
* contract that provides a firewalled method is required in order to
* test the modifier.
*/
contract FirewalledRuleImplementation is FirewalledRule {

/* Special Functions */

constructor(
OrganizationInterface _organization
)
FirewalledRule(_organization)
public
// solium-disable-next-line no-empty-blocks
{}


/* External Functions */


// solium-disable-next-line no-empty-blocks
function firewalledFn() external view firewalled {}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"test:package": "./npm_package/test/run_npm_package_test.sh",
"make:package": "node ./npm_package/make_package.js",
"prepack": "npm-run-all compile:all make:package",
"make:all": "npm-run-all lint:sol:solium lint:js lint:sol:slither compile:all test:contracts make:package test:package",
"make:all": "npm-run-all lint:sol:solium lint:js compile:all test:contracts make:package test:package",
"ganache-cli": "./tools/run_ganache_cli.sh"
},
"devDependencies": {
Expand Down
36 changes: 36 additions & 0 deletions test/firewalled_rule/constructor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2019 OpenST Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

const { AccountProvider } = require('../test_lib/utils');
const Utils = require('../test_lib/utils.js');

const FirewalledRule = artifacts.require('FirewalledRule');

contract('FirewalledRule::constructor', async () => {
contract('Storage', async (accounts) => {
const accountProvider = new AccountProvider(accounts);

it('Checks that the firewall is enabled by default.', async () => {
const { organization } = await Utils.createOrganization(accountProvider);
const firewalledRule = await FirewalledRule.new(organization.address);

assert.strictEqual(
(await firewalledRule.firewallEnabled.call()),
true,
);
});
});
});
88 changes: 88 additions & 0 deletions test/firewalled_rule/disable_firewall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2019 OpenST Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

const { AccountProvider } = require('../test_lib/utils');
const { Event } = require('../test_lib/event_decoder');
const Utils = require('../test_lib/utils.js');

const FirewalledRule = artifacts.require('FirewalledRule');

contract('FirewalledRule::disableFirewall', async () => {
contract('Negative Tests', async (accounts) => {
const accountProvider = new AccountProvider(accounts);

it('Reverts as non-organization is calling.', async () => {
const { organization } = await Utils.createOrganization(accountProvider);

const firewalledRule = await FirewalledRule.new(organization.address);

await Utils.expectRevert(
firewalledRule.disableFirewall({ from: accountProvider.get() }),
'Should revert as a non-organization is calling.',
'Only the organization is allowed to call this method.',
);
});
});

contract('Positive Paths', async (accounts) => {
const accountProvider = new AccountProvider(accounts);

it('Checks that the firewall is disabled after the call.', async () => {
const {
organization,
organizationOwner,
} = await Utils.createOrganization(accountProvider);

const firewalledRule = await FirewalledRule.new(organization.address);
await firewalledRule.disableFirewall({ from: organizationOwner });

assert.strictEqual(
(await firewalledRule.firewallEnabled.call()),
false,
);
});
});

contract('Events', async (accounts) => {
const accountProvider = new AccountProvider(accounts);

it('Emits FirewallDisabled.', async () => {
const {
organization,
organizationOwner,
} = await Utils.createOrganization(accountProvider);

const firewalledRule = await FirewalledRule.new(organization.address);
const response = await firewalledRule.disableFirewall(
{ from: organizationOwner },
);

const events = Event.decodeTransactionResponse(
response,
);

assert.strictEqual(
events.length,
1,
);

Event.assertEqual(events[0], {
name: 'FirewallDisabled',
args: {},
});
});
});
});
Loading

0 comments on commit 42bc1d4

Please sign in to comment.