Skip to content

Encode and decode MAL (Meta Attack Language) to and from JSON.

License

Notifications You must be signed in to change notification settings

victorazzam/demal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PyPI Build Python License

MAL (Meta Attack Language) to JSON encoding/decoding library and command-line tool.

Convert from MAL to JSON and, as of version 2, from JSON to MAL. Incompatible with the official tool.

Info

Author: Victor Azzam

License: MIT

Latest version: 2.1.1

Requires: Python 3.8 or later

Batteries included: Tests and examples are provided in this repository.

Contents

Usage

Install

~ pip install demal

or

Install from source

~ git clone https://github.com/victorazzam/demal
~ cd demal && pip install .

General usage

https://raw.githubusercontent.com/victorazzam/demal/main/usage.jpg

Convert file.mal to file.mal.json

~ demal file.mal

Convert file.mal to file.json

~ demal file.mal file.json

Convert file.json to file.mal (if JSON abides by demal's output)

~ demal file.json file.mal -r

Convert file.mal and print it out

~ demal file.mal -

Read from standard input, convert, and print it out

~ cat file.mal | demal - -

Read from standard input, convert, and write to out.json

~ cat file.mal | demal - out.json

Convert several files and view them interactively

~ cat file1.mal file2.mal | demal - - | less

Display debugging information while converting

~ demal tests/test2.mal debug
parse got: '#id: "tmp"'
parse got: '#version: "0.0.0"'
parse got: 'category C1 {'
parse_category got: '}'
parse got: 'category C2'
parse_header got: 'user info: "info"'
parse_header got: '{'
parse_category got: 'asset A1 {'
parse_asset got: '& At1'
parse_asset got: '| At2'
# output truncated

Use it as a Python module

from demal import MalParser
mal = MalParser('threat-model.mal')
mal.debug = True
mal.parse() # displays debugging messages due to the previous line
# output suppressed

mal.dump(out='parsed.json', pretty=True) # beautify and save to parsed.json
print(mal) # pretty-prints the json object
{
  "associations": [
    {
      "asset_l": "Server",
      "asset_l": "Client",
# output truncated

Merge multiple instances by addition (or multiplication or bitwise-or) akin to using include

Check tests/test-lib.py:

from demal import MalParser, sys

print('Parse and combine two test files.')
m1, m2 = MalParser('test1.mal'), MalParser('test2.mal')
m1.parse()
m2.parse()

print('First:', m1['version'])
# First: 1.0.0

print('Second:', m2['version'])
# Second: 0.0.0

m = m1 + m2 # combine category/association items, same as "include"
print('Combined:', m['version'])
# Combined: 0.0.0

print('\nModify variable.')
v = {'version': '1.3.3.7'}
m += v
print('New version:', m['version'])
# New version: 1.3.3.7

print('\nChange inner dictionary data.')
print('Before:', m['categories']['System']['assets']['Host']['attributes']['guessedPassword']['probability'])
# Before: Exponential(0.02)

m['categories']['System']['assets']['Host']['attributes']['guessedPassword']['probability'] = 'abc'
print('After:', m['categories']['System']['assets']['Host']['attributes']['guessedPassword']['probability'])
# After: abc

print('\nList assets with dot notation: category.asset')
print('m1:', list(m1))
print('m2:', list(m2))
print('m (m1+m2):', list(m))
# m1: ['System.Host', 'System.Network', 'System.Password', 'System.User']
# m2: ['C2.A1', 'C2.A2', 'C2.A3', 'C2.A4', 'C2.A5', 'C2.A6', 'C3.A1', 'C4.A1', 'C5.distribution']
# m (m1+m2): ['System.Host', 'System.Network', 'System.Password', 'System.User', 'C2.A1', 'C2.A2', 'C2.A3', 'C2.A4', 'C2.A5', 'C2.A6', 'C3.A1', 'C4.A1', 'C5.distribution']

print('\nConvert JSON back to MAL syntax.')
m.dump_mal(out = sys.stdout)
# Output redacted

Output

The following output JSON structure is produced ("quotes" are placeholders, monospace shows exact values, | denotes "choose one of"):

  • Array: associations
    • Object:
      • Object: meta
        • String: "key"
          • String: "value"
      • String: asset_l, field_l, mult_l, name, mult_r, field_r, asset_r
  • Object: categories
    • Object: "category name"
      • Object: meta
        • String: "key"
          • String: "value"
      • Object: assets
        • Object: meta
          • String: "key"
            • String: "value"
        • Object: "asset name"
          • Boolean: abstract
          • Object: attributes
            • Object: "attribute name"
              • Array: cia
                • String (length=1): C | I | A (MAL: {CIA})
              • Object: meta
                • String: "key"
                  • String: "value"
              • String: probability
                • String (nullable): one of several probability distributions outlined here
              • Array: tags
                • String: hidden | debug | trace (MAL: @hidden @debug @trace)
              • String: type
                • String: or | and | defense | exists | lacks (MAL: | & # E !E)
              • Object: append | leads_to | require (MAL: +> -> <-)
                • String: "key or number"
                  • String: "expression or action"
          • String: extends
            • String (nullable): "asset to be extended"
          • Object: meta
            • String: "key"
              • String: "value"

Example:

{
  "associations": [
    {
      "asset_l": "left_asset",
      "asset_r": "right_asset",
      "field_l": "left_field",
      "field_r": "right_field",
      "meta": {
        "key": "value"
        # ...
      },
      "mult_l": "left_multiplier",
      "mult_r": "right_multiplier",
      "name": "Association"
    } # ...
  ],
  "categories": {
    "Category1": {
      "assets": {
        "Asset1": {
          "abstract": false,
          "attributes": {
            "access": {
              "cia": ["C", "A"],
              "meta": {},
              "probability": "Exponential(0.02)", # etc, or simply null
              "tags": [],
              "type": "and"
            },
            "authenticate": {
              "cia": null,
              "leads_to": {
                "0": "access",
                "a": "b \\/ c " # result of: let a = ...
              },
              "meta": {},
              "probability": null,
              "tags": [
                "hidden",
                "debug",
                "trace"
              ],
              "type": "or"
            } # ...
          },
          "extends": null,
          "meta": {}
        } # ...
      },
      "meta": {}
    }
  },
  "id": "org.name.project",
  "version": "1.0.0"
}

When converting to MAL the output should closely resemble the original, albeit more concise.

Limitations

  • Multi-line comments should be avoided.
  • Quotation marks must not be escaped.
  • Expects clean spec-compliant code.

Resources