seinfoflow/sedta: Add graphical output files.

Add the -o option to output graphical results for domain transition
analysis and information flow analysis.

Signed-off-by: Chris PeBenito <pebenito@ieee.org>
This commit is contained in:
Chris PeBenito 2024-01-26 16:25:31 -05:00 committed by Chris PeBenito
parent 9a5ab901b1
commit ecdc7f460c
9 changed files with 68 additions and 39 deletions

View File

@ -54,6 +54,7 @@ jobs:
gettext \
libaudit-dev \
libbz2-dev \
libgraphviz-dev \
libpcre3-dev \
xvfb \
x11-xserver-utils

View File

@ -15,6 +15,7 @@ To run SETools command line tools, the following packages are required:
* Python 3.10+
* NetworkX 2.6+
* pygraphviz
* setuptools
* libselinux
* libsepol 3.2+

View File

@ -45,6 +45,8 @@ Perform a reverse domain transition analysis. The domain transitions will be an
the parent domains, instead of finding the child domains.
.IP "-l LIMIT_TRANS"
Specify the maximum number of domain transitions to output. The default is unlimited.
.IP "-o OUTPUT_PATH"
Generate a graphical representation of the analysis in PNG format at the specified path.
.IP EXCLUDE
A space-separated list of types to exclude from the analysis.

View File

@ -46,6 +46,8 @@ the policy, a limit of 5 or more may be extremely expensive.
Specify the minimum permission weight to consider for the analysis (1-10). The default is 3.
.IP "-l LIMIT_FLOWS"
Specify the maximum number of information flows to output. The default is unlimited.
.IP "-o OUTPUT_PATH"
Generate a graphical representation of the analysis in PNG format at the specified path.
.IP EXCLUDE
A space-separated list of types to exclude from the analysis.

View File

@ -27,6 +27,7 @@ pretty = true
[[tool.mypy.overrides]]
module = ['networkx.*',
'pygraphviz.*',
'pytestqt.*']
ignore_missing_imports = true

48
sedta
View File

@ -10,6 +10,7 @@ import logging
import signal
import warnings
import networkx as nx
import setools
@ -40,6 +41,7 @@ opts.add_argument("-r", "--reverse", action="store_true", default=False,
help="Perform a reverse DTA.")
opts.add_argument("-l", "--limit_trans", default=0, type=int,
help="Limit to the specified number of transitions. Default is unlimited.")
opts.add_argument("-o", "--output_file", help="Output file for graphical results, PNG format.")
opts.add_argument("exclude", help="List of excluded types in the analysis.", nargs="*")
args = parser.parse_args()
@ -82,21 +84,25 @@ try:
g.mode = setools.DomainTransitionAnalysis.Mode.AllPaths
g.depth_limit = args.all_paths
for pathnum, path in enumerate(g.results(), start=1): # type: ignore
print(f"Domain transition path {pathnum}:")
if args.output_file:
pgv = nx.nx_agraph.to_agraph(g.graphical_results())
pgv.draw(path=args.output_file, prog="dot", format="png")
else:
for pathnum, path in enumerate(g.results(), start=1): # type: ignore
print(f"Domain transition path {pathnum}:")
for stepnum, step in enumerate(path, start=1):
if args.full:
print(f"Step {stepnum}: {step:full}\n")
else:
print(f"Step {stepnum}: {step}")
for stepnum, step in enumerate(path, start=1):
if args.full:
print(f"Step {stepnum}: {step:full}\n")
else:
print(f"Step {stepnum}: {step}")
if args.limit_trans and pathnum >= args.limit_trans:
break
if args.limit_trans and pathnum >= args.limit_trans:
break
print()
print()
print(f"\n{pathnum} domain transition path(s) found.")
print(f"\n{pathnum} domain transition path(s) found.")
else: # single transition
if args.reverse:
@ -106,16 +112,20 @@ try:
g.mode = setools.DomainTransitionAnalysis.Mode.TransitionsOut
g.source = args.source
for pathnum, step in enumerate(g.results(), start=1): # type: ignore
if args.full:
print(f"Transition {pathnum}: {step:full}\n")
else:
print(f"Transition {pathnum}: {step}")
if args.output_file:
pgv = nx.nx_agraph.to_agraph(g.graphical_results())
pgv.draw(path=args.output_file, prog="dot", format="png")
else:
for pathnum, step in enumerate(g.results(), start=1): # type: ignore
if args.full:
print(f"Transition {pathnum}: {step:full}\n")
else:
print(f"Transition {pathnum}: {step}")
if args.limit_trans and pathnum >= args.limit_trans:
break
if args.limit_trans and pathnum >= args.limit_trans:
break
print(f"\n{pathnum} domain transition(s) found.")
print(f"\n{pathnum} domain transition(s) found.")
if args.stats:
print("\nGraph statistics:")

View File

@ -4,7 +4,6 @@
# SPDX-License-Identifier: GPL-2.0-only
#
import setools
import argparse
import sys
import logging
@ -12,6 +11,9 @@ import signal
import warnings
from typing import Dict, Optional
import networkx as nx
import setools
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
parser = argparse.ArgumentParser(
@ -53,6 +55,7 @@ opts.add_argument("-l", "--limit_flows", default=0, type=int,
opts.add_argument("-b", "--booleans", default=None,
help="Specify the boolean values to use."
" Options are default, or \"foo:true,bar:false...\"")
opts.add_argument("-o", "--output_file", help="Output file for graphical results, PNG format.")
opts.add_argument("exclude", nargs="*",
help="List of excluded types in the analysis.")
@ -117,18 +120,22 @@ try:
g.mode = setools.InfoFlowAnalysis.Mode.AllPaths
g.depth_limit = args.all_paths
for flownum, flow in enumerate(g.results(), start=1): # type: ignore
print(f"Flow {flownum}:")
for stepnum, step in enumerate(flow, start=1):
if args.full:
print(f" Step {stepnum}: {step:full}\n")
else:
print(f" Step {stepnum}: {step}")
if args.output_file:
pgv = nx.nx_agraph.to_agraph(g.graphical_results())
pgv.draw(path=args.output_file, prog="dot", format="png")
else:
for flownum, flow in enumerate(g.results(), start=1): # type: ignore
print(f"Flow {flownum}:")
for stepnum, step in enumerate(flow, start=1):
if args.full:
print(f" Step {stepnum}: {step:full}\n")
else:
print(f" Step {stepnum}: {step}")
if args.limit_flows and flownum >= args.limit_flows:
break
if args.limit_flows and flownum >= args.limit_flows:
break
print()
print(f"\n{flownum} information flow(s) found.")
else: # single step, direct info flow
if args.reverse:
@ -138,16 +145,20 @@ try:
g.mode = setools.InfoFlowAnalysis.Mode.FlowsOut
g.source = args.source
for flownum, step in enumerate(g.results(), start=1): # type: ignore
if args.full:
print(f"Flow {flownum}: {step:full}\n")
else:
print(f"Flow {flownum}: {step}")
if args.output_file:
pgv = nx.nx_agraph.to_agraph(g.graphical_results())
pgv.draw(path=args.output_file, prog="dot", format="png")
else:
for flownum, step in enumerate(g.results(), start=1): # type: ignore
if args.full:
print(f"Flow {flownum}: {step:full}\n")
else:
print(f"Flow {flownum}: {step}")
if args.limit_flows and flownum >= args.limit_flows:
break
if args.limit_flows and flownum >= args.limit_flows:
break
print(f"\n{flownum} information flow(s) found.")
print(f"\n{flownum} information flow(s) found.")
if args.stats:
print("\nGraph statistics:")

View File

@ -99,7 +99,7 @@ setup(name='setools',
setup_requires=['setuptools', 'Cython>=0.29.14'],
install_requires=['setuptools'],
extras_require={
"analysis": "networkx>=2.6",
"analysis": ["networkx>=2.6", "pygraphviz"],
"test": "tox"
}
)

View File

@ -51,6 +51,7 @@ deps = networkx>=2.6
cython>=0.29.14
pytest>=6.0
PyQt6>=6.0
pygraphviz
pytest-qt
pytest-xvfb
commands_pre = {envpython} setup.py build_ext -i