Skip to content

Commit

Permalink
add metric output, restricitons
Browse files Browse the repository at this point in the history
  • Loading branch information
dolevf committed Jun 10, 2022
1 parent 870fc3f commit e765956
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 32 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ COPY core /app/core
COPY static /app/static
COPY templates /app/templates
COPY temp /app/temp
COPY config.py /app

COPY alfred.py /app
COPY version.py /app
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,26 @@ Ever wanted to have your own version of [OPA's Playground](https://play.openpoli
- Syntax Highlighting
- Coverage Highlighting
- Data / Input / Policy Editor
- Restrict Execution of Builtins, such as: `http.send` or `opa.runtime`
- Download Policy as File / Copy to Clipboard

# Screenshot
<p align="center">
<img src="https://github.com/dolevf/Open-Policy-Agent-Alfred/blob/main/static/images/alfred_view.png?raw=true" width="900px" alt="Alfred"/>
</p>

# Configuration
There is not a whole lot of configurations required for Alfred. If you want to restrict certain builtins from running in policies, you can do so in `config.py`:

```
RESTRICTED_BUILTINS = [
'http.send',
'opa.runtime'
]
```

By default, all builtins are allowed.

# How to Install and Use
## Clone repository
`git clone https://github.com/dolevf/Open-Policy-Agent-Alfred`
Expand Down
46 changes: 26 additions & 20 deletions alfred.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,34 @@ def evaluate():
coverage = request.json.get('coverage', False)

result = []
output = utils.opa_evaluate(policy, inputs, data, coverage)
covered_lines = []
non_covered_lines = []

if 'result' in output:
package_name = utils.get_package_name(policy)
value = output['result'][0]['expressions'][0]['value']
covered_lines= utils.get_coverage(output, covered=True)
non_covered_lines = utils.get_coverage(output, covered=False)
try:
result = utils.get_recursively(value, package_name)[0]
except IndexError:
print('Error obtaining result.')

elif 'errors' in output:
for err in output['errors']:
message = err['message']
row = err['location']['row']
code = err['code']
result.append("{0}: {1}: {2} ".format(row, code, message))

return jsonify({"result":result, "coverage":covered_lines, "no_coverage":non_covered_lines})
query_eval_ns = None

policy_violation_name = utils.check_security_policy(policy)
if policy_violation_name:
result = ['{} is not allowed to be executed!'.format(policy_violation_name)]
else:
output = utils.opa_evaluate(policy, inputs, data, coverage)
if 'result' in output:
package_name = utils.get_package_name(policy)
value = output['result'][0]['expressions'][0]['value']
query_eval_ns = utils.get_query_eval_ns(output)
covered_lines= utils.get_coverage(output, covered=True)
non_covered_lines = utils.get_coverage(output, covered=False)
try:
result = utils.get_recursively(value, package_name)[0]
except IndexError:
print('Error obtaining result.')

elif 'errors' in output:
for err in output['errors']:
message = err['message']
row = err['location']['row']
code = err['code']
result.append("{0}: {1}: {2} ".format(row, code, message))

return jsonify({"result":result, "coverage":covered_lines, "no_coverage":non_covered_lines, "query_eval_ns":query_eval_ns})

@app.context_processor
def versions():
Expand Down
8 changes: 8 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Buitins that should never be executed (Security)
# Add any potentially dangerous functions here.
# The full list of builtins can be found at: https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions

RESTRICTED_BUILTINS = [
# 'http.send',
# 'opa.runtime'
]
21 changes: 17 additions & 4 deletions core/utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import os
import json
import tempfile
import config

tempfile.tempdir = "./temp"

def run_cmd(cmd):
return os.popen(cmd).read()

def check_security_policy(policy):
allowed = False
for line in policy.splitlines():
if not line.startswith('#'):
for key in config.RESTRICTED_BUILTINS:
if key in line.strip():
allowed = key
return allowed

def get_recursively(search_dict, field):
fields_found = []

Expand All @@ -31,10 +41,10 @@ def get_recursively(search_dict, field):
def get_coverage(result, covered=True):
coverage = []
key = 'covered'

if not covered:
key = 'not_covered'

files = result.get('coverage', {}).get('files', [])
for f in files:
if key in files[f]:
Expand All @@ -45,6 +55,9 @@ def get_coverage(result, covered=True):

return coverage

def get_query_eval_ns(result):
return result.get('metrics', {}).get('timer_rego_query_eval_ns', None)

def get_package_name(text):
key_name = None
for line in text.splitlines():
Expand Down Expand Up @@ -85,9 +98,9 @@ def opa_evaluate(policy, inputs, data=' ', coverage=False):
res = None

if data:
res = run_cmd(f"./bin/opa eval -d {data_file} -i {input_file} -d {policy_file} data {args}")
res = run_cmd(f"./bin/opa eval -d {data_file} -i {input_file} -d {policy_file} --profile data {args}")
else:
res = run_cmd(f"./bin/opa eval -i {input_file} -d {policy_file} data {args}")
res = run_cmd(f"./bin/opa eval -i {input_file} -d {policy_file} --profile data {args}")

try:
res = json.loads(res)
Expand Down
13 changes: 6 additions & 7 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Open Policy Agent Sandbox</title>
<title>Open Policy Agent Alfred</title>
<link rel="icon" type="image/x-icon" href="static/assets/favicon.ico" />
<link href="static/css/styles.css" rel="stylesheet" />
<link href="static/css/opa.css" rel="stylesheet" />
Expand Down Expand Up @@ -85,26 +85,24 @@
<div class="col-md-12">
<div class="well">
<div class="form-group">
<label for="result-area"><b>Result</b></label>
<label for="result-area"><b>Result</b> <span id="query_eval_ns" style="color:rgb(107, 101, 101); font-size: x-small; font-style: italic;"></span></label>
<div id="editorresult"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="btn-group" role="group" aria-label="Basic example">
<div class="btn-group" role="group">
<button type="button" class="btn btn-success btn-sm" style="margin-right: 3px;" onclick="query();" title="Evaluate Policy with input and data">Evaluate</button>
<button type="button" class="btn btn-danger btn-sm mr-1" style="margin-right: 3px;" onclick="clearPolicyEditor();" title="Clears Policy Window">Clear</button>
<button type="button" class="btn btn-primary btn-sm mr-1" style="margin-right: 3px;" onclick="copyPolicyEditor();" title="Copy Policy Code to Clipboard">Copy to Clipboard</button>
<button type="button" class="btn btn-secondary btn-sm mr-1" style="margin-right: 3px;" onclick="download_policy();" title="Download Policy to Disk">Download Policy</button>
</div>
</div>

<!-- Bootstrap core JS-->

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- Core theme JS-->
<script src="/static/js/scripts.js"></script>
<script src="/static/assets/ace-builds/src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var editor = ace.edit("editor");
Expand Down Expand Up @@ -135,7 +133,7 @@
for (let item of prevMarkersArr) {
editor.session.removeMarker(prevMarkers[item].id);
}
}
}
}

function copyPolicyEditor() {
Expand All @@ -160,6 +158,7 @@
if (xhr.readyState === 4 && xhr.status === 200) {
var resp = JSON.parse(xhr.responseText);
editorresult.setValue(JSON.stringify(resp.result, null, 2), 1)
document.getElementById("query_eval_ns").textContent = resp.query_eval_ns + " ns";

if (coverage == true);
var Range = ace.require('ace/range').Range;
Expand Down
2 changes: 1 addition & 1 deletion version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = '1.0.4'
VERSION = '1.0.5'

0 comments on commit e765956

Please sign in to comment.