Skip to content

Commit

Permalink
added supported grammar and update README
Browse files Browse the repository at this point in the history
  • Loading branch information
chyanju committed Mar 5, 2024
1 parent 656ba6e commit 445d745
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 36 deletions.
85 changes: 83 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

This repo hosts an open-source Python branch of the static analysis tool Vanguard developed by Veridise. This version is optimized for analyzing Leo/Aleo programs.

## Table of Contents

- [Prerequisites](#prerequisites)
- [Usage](#usage)
- [Commandline Executable](#commandline-executable)
- [Example Commands](#example-commands)
- [Calling from Source](#calling-from-source)
- [Calling as Library](#calling-as-library)
- [Detectors Available](#detectors-available)
- [Example Leo/Aleo Vulnerabilities](#example-leoaleo-vulnerabilities)
- [Parser/Lexer Generation](#parserlexer-generation)
- [Test Suite and Static Analysis APIs](#test-suite-and-static-analysis-apis)

## Prerequisites

The following libraries are required for running (different components of) the tool:
Expand All @@ -17,9 +30,77 @@ The following libraries are required for running (different components of) the t
- <u>Leo (**7ac50d8**) for compiling and running all benchmarks enclosed</u>
- The tools is tested under this version, but newer version of Lao may also work.

## Vanguard for Aleo
## Usage

The library of Vanguard for Aleo provides common vulnerability detectors and basic utilities for writing detectors based on static analysis. There are three ways to use and integrate the tool into your workflow, namely: commandline executable, calling from source and calling as library.

### Commandline Executable

The analyzer can be installed via `pip` setup tools by running:

```bash
pip install .
```

and if you want to remove it:

```bash
pip uninstall vanguard
```

After installation, you can directly use the commandline executable `vanguard-aleo` provided:

```bash
usage: vanguard-aleo [-h] [-b BUILD] [-p PID] [-f FIDS] [-d {divz,infoleak,rtcnst,unused}] [-v]

options:
-h, --help show this help message and exit
-b BUILD, --build BUILD
project build path, default: ./
-p PID, --pid PID program id, default: <project main entrance>
-f FIDS, --fids FIDS function ids (separated by comma, no space), default: <all functions of project>
-d {divz,infoleak,rtcnst,unused}, --detector {divz,infoleak,rtcnst,unused}
detector to use, default: infoleak
-v, --verbose whether or not to return extra info, default: False
```

#### Example Commands

- Test detector `infoleak` on all functions of the main program of a project:

```bash
vanguard-aleo -b ./tests/public/infoleak0/build/ -d infoleak
```

- Test detector `infoleak` on function `ex0` of the main program of a project:

```bash
vanguard-aleo -b ./tests/public/infoleak0/build/ -f ex0 -d infoleak
```

- Test detector `infoleak` of multiple functions `ex0`, `ex1` and `ex2` of the program `infoleak0.aleo`:

```bash
vanguard-aleo -b ./tests/public/infoleak0/build/ -f ex0,ex1,ex2 -p infoleak0.aleo -d infoleak
```

- Test detector `infoleak` of multiple functions `ex0`, `ex1` and `ex2` of the program `infoleak0.aleo`, and print out extra information about the finding:

```bash
vanguard-aleo -b ./tests/public/infoleak0/build/ -f ex0,ex1,ex2 -p infoleak0.aleo -d infoleak -v
```

This will produce the following output:

```
| id | program | function | detector | result | info |
|------|----------------|------------|------------|----------|----------------|
| 0 | infoleak0.aleo | ex0 | infoleak | unsafe | [('r0', 'r0')] |
| 1 | infoleak0.aleo | ex1 | infoleak | safe | [] |
| 2 | infoleak0.aleo | ex2 | infoleak | unsafe | [('r0', 'r1')] |
```

The library of Vanguard for Aleo provides common vulnerability detectors and basic utilities for writing detectors based on static analysis. To use the tool, you can call it directly from the repo or install it as a library.
where the info column provides more information about the detected vulnerability. For example, in function `ex0` there's information leakage from variable `r0` to `r0` (direct returning of input), and in `ex2` from `r0` to `r1`.

### Calling from Source

Expand Down
68 changes: 35 additions & 33 deletions tests/test4.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"id": "b34815c6-9610-4ef1-a7d2-4ee62cd30ae7",
"metadata": {},
"outputs": [],
Expand Down Expand Up @@ -96,48 +96,50 @@
"output_type": "stream",
"text": [
"# [debug] deploy: main.aleo\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex0, expected: True, actual: True\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex1, expected: False, actual: False\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex2, expected: True, actual: True\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex3, expected: True, actual: True\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex4, expected: True, actual: True\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex5, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex6, expected: True, actual: False\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex7, expected: False, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex8, expected: True, actual: False\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex9, expected: True, actual: True\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex10, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex11, expected: False, actual: True\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex12, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex13, expected: True, actual: False\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex14, expected: True, actual: True\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex15, expected: True, actual: True\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex16, expected: True, actual: True\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex17, expected: True, actual: False\n",
"# [✓][test] pid: infoleak0.aleo, fid: ex18, expected: True, actual: True\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex19, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex20, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex21, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex22, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex23, expected: True, actual: False\n",
"# [✗][test] pid: infoleak0.aleo, fid: ex24, expected: True, actual: False\n",
"# [test] accuracy: 11/25 (0.4400)\n",
"# [debug] deploy: helpers.aleo\n",
"# [✓][test] pid: divz0.aleo, fid: ex0, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex1, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex2, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex3, expected: True, actual: True\n",
"# [✗][test] pid: divz0.aleo, fid: ex4, expected: False, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex5, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex6, expected: True, actual: True\n",
"# [✗][test] pid: divz0.aleo, fid: ex7, expected: False, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex8, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex9, expected: False, actual: False\n",
"# [✓][test] pid: divz0.aleo, fid: ex10, expected: False, actual: False\n",
"# [✓][test] pid: divz0.aleo, fid: ex11, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex12, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex13, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex14, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex15, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex16, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex17, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex18, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex19, expected: True, actual: True\n",
"# [✗][test] pid: divz0.aleo, fid: ex20, expected: True, actual: False\n",
"# [✓][test] pid: divz0.aleo, fid: ex21, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex22, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex23, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex24, expected: True, actual: True\n",
"# [✓][test] pid: divz0.aleo, fid: ex25, expected: True, actual: True\n",
"# [test] accuracy: 23/26 (0.8846)\n",
"# [test] confusion matrix:\n",
" actual False True \n",
"expected \n",
"False 2 1\n",
"True 13 9\n",
"False 2 2\n",
"True 1 21\n",
"# [test] normalized confusion matrix:\n",
" actual False True \n",
"expected \n",
"False 0.666667 0.333333\n",
"True 0.590909 0.409091\n"
"False 0.500000 0.500000\n",
"True 0.045455 0.954545\n"
]
}
],
"source": [
"# r = run_test_suite(\"./tests/public/divz0/build/\", detector_divz, verbose=True)\n",
"r = run_test_suite(\"./tests/public/infoleak0/build/\", detector_infoleak, verbose=True)\n",
"r = run_test_suite(\"./tests/public/divz0/build/\", detector_divz, verbose=True)\n",
"# r = run_test_suite(\"./tests/public/infoleak0/build/\", detector_infoleak, verbose=True)\n",
"# r = run_test_suite(\"./tests/public/rtcnst0/build/\", detector_rtcnst, verbose=True)\n",
"# r = run_test_suite(\"./tests/public/unused0/build/\", detector_unused, verbose=True)"
]
Expand Down
3 changes: 3 additions & 0 deletions vanguard/aleo/detectors/divz.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ def a(node):
return node
case AleoLiteral():
return AleoAbstractLiteral.abs(node)
case [*_]:
# collection, directly return since each element should've been visited already
return node
case _:
raise NotImplementedError(f"Can't wrap a non-literal, got: {node}")

Expand Down
66 changes: 66 additions & 0 deletions vanguard/aleo/grammar/instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def from_json(node):
return AleoGet.from_json(node[1])
case ["command", ["get_or_use", *_]]:
return AleoGetOrUse.from_json(node[1])
case ["command", ["remove", *_]]:
return AleoRemove.from_json(node[1])
case ["command", ["xawait", *_]]:
return AleoAwait.from_json(node[1])
case _:
raise NotImplementedError(f"Unsupported json component, got: {node}")

Expand Down Expand Up @@ -153,6 +157,44 @@ def __init__(self, id, operand, default, regacc):

def __str__(self):
return f"get.or_use {self.id}[{self.operand}] {self.default} into {self.regacc};"

class AleoRemove(AleoCommand):

@staticmethod
def from_json(node):
match node:
case ["remove", "remove", id, "[", operand, "]", ";"]:
_id = AleoIdentifier.from_json(id)
_operand = AleoOperand.from_json(operand)
return AleoRemove(_id, _operand)
case _:
raise NotImplementedError(f"Unsupported json component, got: {node}")

def __init__(self, id, operand, **kwargs):
super().__init__(**kwargs)
self.id = id
self.operand = operand

def __str__(self):
return f"remove {self.id}[{self.operand}];"

class AleoAwait(AleoCommand):

@staticmethod
def from_json(node):
match node:
case ["xawait", "await", regacc, ";"]:
_regacc = AleoRegisterAccess.from_json(regacc)
return AleoAwait(_regacc)
case _:
raise NotImplementedError(f"Unsupported json component, got: {node}")

def __init__(self, regacc, **kwargs):
super().__init__(**kwargs)
self.regacc = regacc

def __str__(self):
return f"await {self.regacc};"

class AleoInstruction(AleoCommand):

Expand All @@ -177,6 +219,8 @@ def from_json(inst):
return AleoTernary.from_json(inst[1])
case ["instruction", ["hash", *_]]:
return AleoHash.from_json(inst[1])
case ["instruction", ["sign_verify", *_]]:
return AleoSignVerify.from_json(inst[1])
case _:
raise NotImplementedError(f"Unsupported json component, got: {inst}")

Expand Down Expand Up @@ -238,6 +282,7 @@ def from_json(node):
raise NotImplementedError(f"Unsupported json component, got: {node}")

def __init__(self, op, operand, regacc, **kwargs):
super().__init__(**kwargs)
self.op = op
self.operand = operand
self.regacc = regacc
Expand Down Expand Up @@ -452,3 +497,24 @@ def __init__(self, op, operand, regacc, type, **kwargs):

def __str__(self):
return f"{self.op} {self.operand} into {self.regacc} as {self.type}"

class AleoSignVerify(AleoInstruction):

@staticmethod
def from_json(node):
match node:
case ["sign_verify", ["sign_verify_op", *_], *operands, "into", regacc, ";"]:
_operands = [ AleoOperand.from_json(p) for p in operands ]
_regacc = AleoRegisterAccess.from_json(regacc)
return AleoSignVerify(_operands, _regacc)
case _:
raise NotImplementedError(f"Unsupported json component, got: {node}")

def __init__(self, operands, regacc, **kwargs):
super().__init__(**kwargs)
self.operands = operands
self.regacc = regacc

def __str__(self):
_operands = " ".join([f"{p}" for p in self.operands])
return f"sign.verify {_operands} into {self.regacc};"
8 changes: 7 additions & 1 deletion vanguard/aleo/grammar/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def from_json(node):
return AleoBooleanType.from_json(node[1])
case ["literal_type", ["address_type", *_]]:
return AleoAddressType.from_json(node[1])
case ["literal_type", ["signature_type", *_]]:
return AleoSignatureType.from_json(node[1])
case _:
raise NotImplementedError(f"Unsupported json component, got: {node}")

Expand All @@ -108,7 +110,6 @@ def __init__(self, **kwargs):
def __str__(self):
raise TypeError()


class AleoBooleanType(AleoLiteralType):

@staticmethod
Expand Down Expand Up @@ -363,6 +364,11 @@ def from_json(node):
_visibility = AleoModifier.from_json(visibility)
_type.visibility = _visibility
return _type
case ["finalize_type", ["locator", *_], modifier]:
_type = AleoLocator.from_json(node[1])
_visibility = AleoModifier.from_json(modifier)
_type.visibility = modifier
return _type
case _:
raise NotImplementedError(f"Unsupported json component, got: {node}")

Expand Down
6 changes: 6 additions & 0 deletions vanguard/aleo/graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ def get_dfg_edges(env: AleoEnvironment, pid: str, fid: str, hash=True, call=Fals
# no data flow
pass

case AleoSignVerify():
for p in inst.operands:
if not isinstance(p, AleoLiteral):
# only consider non-literal
edges.append((p, inst.regacc))

case _:
raise NotImplementedError(f"Unsupported instruction, got: {inst}, type: {type(inst)}")

Expand Down

0 comments on commit 445d745

Please sign in to comment.