npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@shield3/banyan

v0.3.13

Published

Banyan policy engine

Downloads

8

Readme

All About Policies

Anatomy of a Policy

Banyan Policy Language

Banyan is an adaptation of AWS' Cedar Policy Language (CPL). Banyan adapts CPL for use in EVM transaction permissioning. You can get a firm understanding on the cedar policy language in this tutorial. A CPL policy statement is made up of the following:

  • The effect (forbid, permit)
  • The scope (principal, action, resource)
  • Conditions (unless, when)

A cedar file can contain a series of policy statements. Once a series of policy statements are executed, it will result in either "ALLOW" or "DENY". By default, everything not explicitly permitted results in "DENY", but this can be changed. See "Setting Base Authorization". In instances where policies are conflicting, they will also return "DENY".

Banyan makes use of decorators to extend the functionality of the Cedar Policy Language. A policy written in Banyan will follow the format in the example below.

@name("Policy Statement 1 Name")
@message("This is the message returned when this policy fulfills it's action.")
@dependency("threatmodels.alerts")
@action("MFA")
permit(
    principal,
    action == 0xasldfakj,
    resource
) when {context.transaction.value.u256LessThan(u256("1234")) && threatmodels.alerts.time.u256LessThan(time.now()-time.hours(72))};

Contextual information is provided inside the json request.

Decorators

@name

This decorator is used to define and name a policy statement. This name will make an appearance in the Shield3 UI, messages sent to you regarding this policy, and can be used to reference the policy elsewhere.

@name("Known phishing address")
@message

When the policy statement triggers a message, the user will receive this message. The message decorator was designed to be used as a way of warning against potentially risky situations.

@message("Your transaction was blocked because the receiver of the transfer is a known phishing scammer. Please be sure to verify addresses before sending.")
@action

This is the action to carry out if the policy statement returns the relevant permission. The action can be defined as either Multi-Factor Authentication (MFA) or Notify. A DENY always results in block, however, ALLOW can result in either pass, notify, or MFA.

Block vs. Pass

Blocking means a transaction is not forwarded to the node service provider. If a transaction is blocked, a notification is sent to the transaction executor with the message defined in the @message decorator. If the transaction passes, then it is silently forwarded to the node service provider.

MFA

An MFA action is triggered when it's policy statement returns ALLOW. When MFA is triggered, the policy statement name, policy statement message, and transaction context will be sent to the transaction executor through their configured notifications, which can be found here.

The executor will then have the option to allow or deny the transaction. As shown previously, it's a good idea to set the message decorator as a warning, describing the risks coming with approving the transaction.

@action("MFA")
NOTIFY

When the notify action is triggered, the transaction will be broadcast as per usual, but notify the executor with the message provided from the message decorator.

@action("NOTIFY")
@dependency
@dependency("abcd-1234")

Any transaction context you'd like to reference can be found in your json request. Below is an example request, and a condition referencing it. In this case it returns a boolean to check if the network is mainnet ethereum. Any rich context you'd like to reference (information other than what is encoded in your transaction) must be defined as a dependency using the @dependency decorator. This rich context is then found in entities.json.

request.json

{
  "principal": "Address::\"0xcfcdec1645234f521f29cb2bb0d57a539ba3bfae\"",
  "action": "Action::\"eoa\"",
  "resource": "Address::\"0x7c3250001bc0abeeef91f52e9054a9f951190132\"",
  "context": {
    "transaction": {
      "network": {
        "__entity": {
          "type": "Network",
          "id": "0x01"
        }
      },
      "data": "0x",
      "value": {
        "__expr": "u256(\"740048210\")"
      },
      "gasLimit": {
        "__expr": "u256(\"500000\")"
      }
    }
  }
}

entities.json

[
  {
    "uid": {
      "type": "Address",
      "id": "0xd8a53b315823d8f8df8cb438c13ebe08af7c9ca9"
    },
    "attrs": {},
    "parents": []
  },
  {
    "uid": {
      "type": "Address",
      "id": "0x7a59293fe5fc36fdd762b4daeb07ba0873a3de44"
    },
    "attrs": {
      "groups": [
        {
          "__entity": {
            "type": "Group",
            "id": "1f033d2d-461a-4ce4-9026-5eb7efff5b4a"
          }
        }
      ]
    },
    "parents": []
  },
  {
    "uid": {
      "type": "Address",
      "id": "0xcfcdec1645234f521f29cb2bb0d57a539ba3bfae"
    },
    "attrs": {},
    "parents": []
  },
  {
    "uid": {
      "type": "Address",
      "id": "0x7c3250001bc0abeeef91f52e9054a9f951190132"
    },
    "attrs": {},
    "parents": []
  },
  {
    "uid": {
      "type": "Group",
      "id": "1f033d2d-461a-4ce4-9026-5eb7efff5b4a"
    },
    "attrs": {},
    "parents": []
  },
  {
    "uid": {
      "type": "Network",
      "id": "0x01"
    },
    "attrs": {
      "blockNumber": 18372931
    },
    "parents": []
  }
]

policies.cedar

@name("Base Permit")
permit(
		principal,
		action,
		resource
);


@name("Sanctions")
@message("Block Sanctioned Addresses")
@action("Block")
@dependency("verified_addresses:1f033d2d-461a-4ce4-9026-5eb7efff5b4a")
forbid(
		principal,
		action,
		resource
) when {  resource has groups && resource.groups.contains(Group::"1f033d2d-461a-4ce4-9026-5eb7efff5b4a") };

Recommended Policy Book

  • Setting default authorization
  • No approvals to EOAs
  • No unlimited approvals
  • Approve up to X $USD
  • Block approval on list of token(s)
  • Only have X concurrent approvals per token
  • No transactions to addresses that are in a group with a scam/sanctionned address
  • MFA on transactions with addresses that have been identified (victim or attacker) in a forta alert in the last x days (5?)
  • Address Whitelist/Blacklist
  • Chain Whitelist/Blacklist
  • MFA to unknown contracts
  • Block unverified contracts
  • Only interact with addresses aged x+ blocks
  • Only interact with addresses with address with x+ activity
  • Only allow methods previously executed x+ times
  • Block transactions to contracts with recent proxy implementation changes
  • Block transactions to contracts with members recently added to it's group

Setting Default Authorization

If left unspecified, anything that is not explicitly allowed will automatically be denied. To make all unspecified requests allowed, simply add this to policies.cedar:

@name("Default Permit")
permit(
		principal,
		action,
		resource
);

No approvals to EOAs

@name("Block approvals to EOAs")
@message("This transaction was blocked because the address you're attempting to approve an EOA to spend your tokens")
forbid(
		principal,
		action,
		resource
) when {resource.method==
"Approve" && resource.contract.data=="0x"};

No unlimited approvals

@name("No unlimited approvals")
@message("Unlimited approvals are not allowed. Set an approval limit")
forbid(
		principal,
		action,
		resource
) when {resource.method==
"Approve" && context.transaction.approve_value.u256Equals(u256(2^256)) };

Approve up to X $USD

@name("Approve up to X Dollars")
@message("The approval in this transaction exceeds the configured max amount in USD.")
forbid(
		principal,
		action,
		resource
) when {resource.method==
"Approve" && context.transaction.approve_value.USD.u256GreaterThan(u256("123")) };

Block approval on list of tokens

@name("Block approval on list of tokens")
@message("The approval in this transaction exceeds the configured max amount in USD.")
forbid(
		principal,
		action,
		resource
) when {resource.method==
"Approve" && context.transaction.to.in(['0x123','0x456','0x789']) };

Max Concurrent Approvals

@name("Max Concurrent Approvals")
@message("This approval would cause you to exceed the maximum number of concurrent approvals allowed for this token.")
@dependency("count of approvals from executor on the to address")
forbid(
		principal,
		action,
		resource
) 
when {
    resource.method=="Approve" &&
    dependencyxyz.u256GreaterThanEqual("X") 
}
unless {resource.value.u256Equals('0')};