Skip to content

Commit

Permalink
Feat: Add filter command.
Browse files Browse the repository at this point in the history
Signed-off-by: Caroline Russell <caroline@appthreat.dev>
  • Loading branch information
cerrussell committed Mar 27, 2024
1 parent 347f1f4 commit 6a4a7c1
Show file tree
Hide file tree
Showing 19 changed files with 11,960 additions and 10,751 deletions.
140 changes: 119 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,26 @@ Collection of tools for use with slices generated by [AppThreat/atom](https://gi

## Install atom

This program does not generate slices; its purpose is to manipulate slices
generated by atom. The current documentation for atom is housed in the
[AppThreat/atom](https://github.com/AppThreat/atom?tab=readme-ov-file) GitHub
repository.
This program does not generate slices; its purpose is to manipulate slices generated by atom. The
current documentation for atom is housed in the [AppThreat/atom](https://github.com/AppThreat/atom?tab=readme-ov-file) GitHub repository.

Atom can easily be installed from a
[native image](https://github.com/AppThreat/atom#atom-native-image) or via npm
`npm install @appthreat/atom`.
Atom can easily be installed from a [native image](https://github.com/AppThreat/atom#atom-native-image) or via npm `npm install -g @appthreat/atom`.

## Atom-tools installation

`pip install atom-tools`

## CLI Usage

Atom-tools uses py-poetry/cleo to construct its command-line interface and
therefore uses the same sorts of conventions as the package management utility
poetry.
Atom-tools uses py-poetry/cleo to construct its command-line interface and therefore uses the same
sorts of conventions as the Python package management utility poetry.

To access the commands help menu, enter `atom-tools list` for a list of
available commands.
To access the commands help menu, enter `atom-tools list` for a list of available commands.

Individual command options can be accessed with `atom help ` and the command
name (e.g. `atom help convert`).
Individual command options can be accessed with `atom-tools help ` and the command name (e.g. `atom-tools help
convert`).
```
Atom Tools (version 0.4.0)
Atom Tools (version 0.5.0)
Usage:
command [options] [arguments]
Expand All @@ -43,18 +37,122 @@ Options:
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug.
Available commands:
convert Convert an atom slice to a different format
convert Convert an atom slice to a different format.
filter Filter an atom slice based on specified criteria.
help Displays help for a command.
list Lists commands.
validate-lines Check the accuracy of the line numbers in an atom slice.
```

## Features
### Filter
The filter command can be run on its own to produce a filtered slice or used before another command
to filter a slice before executing another command on it. Please note that for line numbers, the
strategy is to include any slice in its entirety which includes the given line number or line
number range in one of its fields.

#### Chaining filter commands
The filter command can act on itself by specifying an additional filter command as an argument.
This may desirable for certain use cases where one wishes some criteria to be required.

**Example**

`atom-tools filter -i slices.json --criteria filename=myfile -e "filter --criteria resolvedMethod=mymethod,resolvedMethod=mymethod2 convert"`

This would be equivalent to

`if fileName == 'myfile' and (resolvedMethod == 'mymethod' or resolvedMethod == 'mymethod2'):`


#### Available attributes (not case-sensitive):
- fileName
- fullName
- resolvedMethod
- callName
- name
- signature

| attribute | locations |
|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | objectSlices.usages.targetObj, objectSlices.usages.definedBy, userDefinedTypes.fields |
| callName | objectSlices.usages.targetObj, objectSlices.usages.definedBy, userDefinedTypes.procedures, |
| resolvedMethod | objectSlices.usages.targetObj, objectSlices.usages.definedBy, objectSlices.usages.argToCalls, objectSlices.usages.invokedCalls, userDefinedTypes.procedures |
| fullName | objectSlices |
| signature | objectSlices |
| fileName | objectSlices, userDefinedTypes | |

#### Criteria syntax
Multiple criteria can be given by using a comma as a separator (no space)

`--criteria [attribute]=[value],[attribute2]=[value],...`

Examples:
`--criteria filename=server.ts`
`--criteria filename=server.ts,filename=server.ts`

#### Usage

```
Description:
Filter an atom slice based on specified criteria.
Usage:
filter [options] [--] <slice-file> <cmd>
Arguments:
slice-file The atom slice file to filter.
cmd
Options:
-c, --criteria=CRITERIA Filter based on an attribute of the slice. Please see documentation for syntax and available attributes.
-o, --outfile=OUTFILE File to re-export filtered slice to.
--strict Strict mode filters on all attributes (must match all rather than any).
-h, --help Display help for the given command. When no command is given display help for the list command.
-q, --quiet Do not output any message.
-V, --version Display this application version.
--ansi Force ANSI output.
--no-ansi Disable ANSI output.
-n, --no-interaction Do not ask any interactive question.
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug.
Help:
The filter command filters an atom slice based on specified criteria.
```
#### Examples
***Filter with the convert command.***

`atom-tools filter -i usages.slices.json --criteria fileName=server.ts -e "convert -f openapi3.0.1 -o openapi_usages.json -t java"`

The above will produce an OpenAPI document based only on slices generated from server.ts.

This can be expanded upon by including a line number or range of line numbers with the filename.

`atom-tools filter -i usages.slices.json --criteria fileName=server.ts:220-250 -e "convert -f openapi3.0.1 -o openapi_usages.json -t java"`

The above can be used to identify endpoints located between lines 220-250 in server.ts.

***Filter based on another attribute***

`atom-tools -i usages.slices.json filter --criteria resolvedMethod=validateSignup`

The above will create a filtered json that only includes slices where the resolved method equals "validateSignup". Since no command is specified, the filtered
slice will only be written to file.

>Filtering can also be used to exclude. The first example could be changed to exclude server.ts with the following:
>>`atom-tools filter --criteria fileName!=server.ts usages.slices.json convert -f openapi3.0.1 -o openapi_usages.json -t java `
>Multiple filter criteria may be included. The following example will produce a filtered slice based only on server.ts slices and exclude calls to addUser.
> >`atom-tools filter --criteria fileName=server.ts,callName!=addUser usages.slices.json`
>Note that the input slice should not be specified for commands used with filter as the new filtered slice will be the source. If they are specified, atom-tools will replace.

### Convert
The convert command can be used to output an atom slice in a different format.
The current capabilities are limited to processing usages in order to generate
endpoints for an openapi 3.x paths object. Future iterations will populate the
path item objects with more details based on atom slices.
The convert command can be used to output an atom slice in a different format. The current
capabilities are limited to processing usages in order to generate endpoints for an openapi 3.x
paths object. Future iterations will populate the path item objects with more details based on
atom slices.

```
Description:
Expand Down Expand Up @@ -118,4 +216,4 @@ Help:
```

**Example**
>`atom-tools -v validate-lines -t java -j project_json_report.json -i usages.slices.json -d /home/my_project_dir`
>`atom-tools validate-lines -t java -j project_json_report.json -i usages.slices.json -d /home/my_project_dir`
2 changes: 1 addition & 1 deletion atom_tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""
A cli, classes and functions for converting an atom slice to a different format
"""
__version__ = '0.4.5'
__version__ = '0.5.0'
35 changes: 30 additions & 5 deletions atom_tools/cli/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Base console application.
"""
import logging
import sys
from importlib import import_module
from typing import Callable

Expand All @@ -10,7 +11,11 @@
from cleo.events.console_events import COMMAND
from cleo.events.event import Event
from cleo.events.event_dispatcher import EventDispatcher
from cleo.io.inputs.argv_input import ArgvInput
from cleo.io.inputs.input import Input
from cleo.io.io import IO
from cleo.io.outputs.output import Output
from cleo.io.outputs.stream_output import StreamOutput

from atom_tools import __version__
from atom_tools.cli.command_loader import CommandLoader
Expand Down Expand Up @@ -48,6 +53,7 @@ def _load() -> Command:

COMMANDS = [
'convert',
'filter',
'validate-lines',
]

Expand All @@ -71,15 +77,33 @@ def __init__(self) -> None:
command_loader = CommandLoader({name: load_command(name) for name in COMMANDS})
self.set_command_loader(command_loader)

def create_io(
self,
input: Input | None = None, # pylint: disable=redefined-builtin
output: Output | None = None,
error_output: Output | None = None,
) -> IO:
if not input:
input = ArgvInput()
input.set_stream(sys.stdin)

if output is None:
output = StreamOutput(sys.stdout)

if error_output is None:
error_output = StreamOutput(sys.stderr)

return IO(input, output, error_output)

@staticmethod
def register_command_loggers(event: Event, event_name: str, _: EventDispatcher) -> None: # pylint: disable=unused-argument
"""
Registers the command loggers.
Register command loggers. Based heavily on Poetry's implementation.
Args:
event (Event): The event.
event_name (str): The event name.
_: EventDispatcher: The event dispatcher.
event (Event): The event object.
event_name (str): The name of the event.
_: The event dispatcher.
"""
assert isinstance(event, ConsoleCommandEvent)
command = event.command
Expand All @@ -98,7 +122,8 @@ def register_command_loggers(event: Event, event_name: str, _: EventDispatcher)

if io.is_debug():
level = logging.DEBUG
elif io.is_very_verbose() or io.is_verbose():
# elif io.is_very_verbose() or io.is_verbose():
else:
level = logging.INFO

logging.basicConfig(level=level, handlers=[handler])
Expand Down
2 changes: 1 addition & 1 deletion atom_tools/cli/commands/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


class Command(BaseCommand):
loggers: ClassVar[list[str]] = []
loggers: ClassVar[list[str]] = ['atom_tools.cli.commands.filter']

def handle(self):
pass
15 changes: 3 additions & 12 deletions atom_tools/cli/commands/convert.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""
Convert Command for the atom-tools CLI.
"""
import json
import logging
import sys

from cleo.helpers import option

from atom_tools.cli.commands.command import Command
from atom_tools.lib.converter import OpenAPI
from atom_tools.lib.utils import export_json


class ConvertCommand(Command):
Expand Down Expand Up @@ -41,16 +41,8 @@ class ConvertCommand(Command):
'i',
'Usages slice file',
flag=False,
default=None,
value_required=True,
),
# option(
# 'reachables-slice',
# 'r',
# 'Reachables slice file',
# flag=False,
# default=None,
# ),
option(
'type',
't',
Expand Down Expand Up @@ -80,7 +72,7 @@ def handle(self):
"""
Executes the convert command and performs the conversion.
"""
supported_types = ['java', 'jar', 'python', 'py', 'javascript', 'js', 'typescript', 'ts']
supported_types = {'java', 'jar', 'python', 'py', 'javascript', 'js', 'typescript', 'ts'}
if self.option('type') not in supported_types:
raise ValueError(f'Unknown origin type: {self.option("type")}')
match self.option('format'):
Expand All @@ -94,8 +86,7 @@ def handle(self):
if not (result := converter.endpoints_to_openapi(self.option('server'))):
logging.warning('No results produced!')
sys.exit(1)
with open(self.option('output-file'), 'w', encoding='utf-8') as f:
json.dump(result, f, indent=4, sort_keys=True)
export_json(result, self.option('output-file'), 4)
logging.info(f'OpenAPI document written to {self.option("output-file")}.')
case _:
raise ValueError(f'Unknown destination format: {self.option("format")}')
Loading

0 comments on commit 6a4a7c1

Please sign in to comment.