mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-09 08:41:34 +00:00
223 lines
10 KiB
Markdown
223 lines
10 KiB
Markdown
# Declarative Job Templates -- Deprecated
|
||
|
||
Provide the ability to maintain job templates, akin to `onefuzz template
|
||
libfuzzer basic` at the service level. The templates include a job
|
||
definition, an arbitrary set of tasks, and an arbitrary set of notification
|
||
configs. The templates are managed at the service level, with job-submission
|
||
time updates using a declarative syntax based on `jsonpatch`.
|
||
|
||
The SDK makes use of template configs, provided by the service, to
|
||
dynamically build Python methods for each template. This process enables the
|
||
automatic argument parser generated by type signatures to automatically
|
||
create the CLI subcommands for the template.
|
||
|
||
## User Experience
|
||
* On `onefuzz login` (or `onefuzz job_templates refresh`), cache the existing set of templates
|
||
* Users can see the supported templates via `onefuzz job_templates submit --help`
|
||
* Users can submit jobs via `onefuzz job_templates submit libfuzzer OSNAME project name build pool --target_exe fuzz.exe`
|
||
* Template configs are refreshed automatically if they are older than 24 hours.
|
||
|
||
Future work:
|
||
* submitting jobs by config. Not everything is easy to express via argparse,
|
||
such as values that begin with `-`. In order to support this, it should be
|
||
trivial to expose a method that takes a json file and submits it.
|
||
|
||
## Admin Experience
|
||
|
||
Administrators can manage their own templates via `onefuzz job_templates
|
||
manage [list,get,update,delete]`.
|
||
|
||
If the runtime configuration for the template changes, users will need to
|
||
refresh their cache to pull the runtime configuration.
|
||
|
||
## Implementation Details
|
||
|
||
A declarative job template includes:
|
||
* a Job (JobConfig, as used by `onefuzz jobs create`)
|
||
* a list of Tasks (TaskConfig, which is used by `onefuzz tasks create`)
|
||
* a list of Notifications (NotificationConfig + container type, akin to what is used by `onefuzz notifications create`)
|
||
* a list of required and optional form fields used to update the aforementioned JobConfig, TaskConfig, and NotificationConfig entries at runtime.
|
||
|
||
The form fields allow for 'add' or 'replace' of basic field data using [jsonpatch](http://jsonpatch.com) semantics.
|
||
|
||
## Example Form Fields
|
||
|
||
This following field named `target_workers`, which is required to be an `int`,
|
||
will optionally (if the request includes it) replace the `target_workers` value
|
||
of in the first task in the template.
|
||
|
||
```python
|
||
UserField(
|
||
name="target_workers",
|
||
required=False,
|
||
type=UserFieldType.Int,
|
||
help="The number of workers to use for this task",
|
||
locations=[
|
||
UserFieldLocation(
|
||
op=UserFieldOperation.replace,
|
||
path="/tasks/0/task/target_workers",
|
||
),
|
||
],
|
||
)
|
||
```
|
||
|
||
## Allowed Data Types
|
||
The data types allowed in configuring arbitrary components in the JobTemplate are:
|
||
|
||
* `bool`
|
||
* `int`
|
||
* `str`
|
||
* `Dict[str, str]`
|
||
* `List[str]`
|
||
|
||
## Referring to Tasks
|
||
|
||
In existing procedural templates, some tasks require that other tasks are
|
||
running before they may be scheduled. For example, in the `libfuzzer`
|
||
procedural template, the `libfuzzer_crash_report` task has a `libfuzzer_fuzz`
|
||
task prerequisite. This is specified via the prerequisite's `task_id`, which is
|
||
a random server-assigned UUID.
|
||
|
||
In procedural templates, the dependency task is simply created before its
|
||
dependent.
|
||
|
||
To support such a reference in `OnefuzzTemplate`, specify the prerequisite task
|
||
by the `u128` representation index in to the list of tasks. Example, to refer
|
||
to the first task, use:
|
||
|
||
```python
|
||
TaskConfig(
|
||
prereq_tasks=[UUID(int=0)],
|
||
...
|
||
)
|
||
```
|
||
|
||
## Hardcoded vs Runtime-specified Container Names
|
||
|
||
To support differentiating _always use "afl-linux" for tools_ vs _ask
|
||
what container to use for setup_, if the container name is blank in the
|
||
template, it will be provided as part of the `JobTemplateConfig` and in the
|
||
resulting `JobTemplateRequest`.
|
||
|
||
## Specifying Notifications in the Template
|
||
|
||
The existing templates support adding a notification config on the command
|
||
line, via `--notification_config`, but the existing templates themselves do
|
||
not include default notifications.
|
||
|
||
Declarative job templates include optional support to configure notifications
|
||
as part of the template, rather than requiring the user provide the
|
||
configuration.
|
||
|
||
Example declarative job template that specifies using the aforementioned
|
||
NotificationConfig for the `unique_reports` containers used in the Job.
|
||
|
||
```python
|
||
JobTemplateNotification(
|
||
container_type=ContainerType.unique_reports,
|
||
notification=NotificationConfig(config=TeamsTemplate(url="https://contoso.com/webhook-url-here")),
|
||
)
|
||
```
|
||
|
||
|
||
## Differences from Existing Templates
|
||
|
||
* Declaratively specifying the allowed values for enums, such as StatsFormat,
|
||
is not supported. Fields must currently use Str, which evaluates to Enum
|
||
value during template rendering, is functional.
|
||
* Existing templates automatically differentiate between Windows and Linux
|
||
tasks. This does not support differentiating between platforms automatically.
|
||
As such, there is a new required parameter to specify the OS.
|
||
|
||
|
||
## From the CLI
|
||
|
||
```
|
||
❯ onefuzz job_templates submit libfuzzer --help
|
||
usage: onefuzz job_templates submit libfuzzer [-h] [-v] [--format {json,raw}] [--query QUERY] [--target_exe TARGET_EXE]
|
||
[--duration DURATION] [--target_workers TARGET_WORKERS] [--vm_count VM_COUNT]
|
||
[--target_options [TARGET_OPTIONS [TARGET_OPTIONS ...]]]
|
||
[--target_env str=str [str=str ...]] [--reboot_after_setup]
|
||
[--check_retry_count CHECK_RETRY_COUNT] [--target_timeout TARGET_TIMEOUT]
|
||
[--tags str=str [str=str ...]] [--readonly_inputs_dir READONLY_INPUTS_DIR]
|
||
[--setup_dir SETUP_DIR] [--inputs_dir INPUTS_DIR]
|
||
[--container_names ContainerType=str [ContainerType=str ...]]
|
||
OS project name build pool_name
|
||
|
||
positional arguments:
|
||
OS Specify the OS to use in the job. accepted OS: windows, linux
|
||
project Name of the Project
|
||
name Name of the Target
|
||
build Name of the Target
|
||
pool_name Execute the task on the specified pool
|
||
|
||
optional arguments:
|
||
-h, --help show this help message and exit
|
||
-v, --verbose increase output verbosity
|
||
--format {json,raw} output format
|
||
--query QUERY JMESPath query string. See http://jmespath.org/ for more information and examples.
|
||
--target_exe TARGET_EXE
|
||
Path to the target executable (default: fuzz.exe)
|
||
--duration DURATION Number of hours to execute the task (default: 24)
|
||
--target_workers TARGET_WORKERS
|
||
Number of instances of the libfuzzer target on each VM
|
||
--vm_count VM_COUNT Number of VMs to use for fuzzing (default: 2)
|
||
--target_options [TARGET_OPTIONS [TARGET_OPTIONS ...]]
|
||
Command line options for the target
|
||
--target_env str=str [str=str ...]
|
||
Environment variables for the target
|
||
--reboot_after_setup After executing the setup script, reboot the VM (Default: False. Sets value to True)
|
||
--check_retry_count CHECK_RETRY_COUNT
|
||
Number of times to retry a crash to verify reproducability
|
||
--target_timeout TARGET_TIMEOUT
|
||
Number of seconds to timeout during reproduction
|
||
--tags str=str [str=str ...]
|
||
User provided metadata for the tasks
|
||
--readonly_inputs_dir READONLY_INPUTS_DIR
|
||
Local path to the readonly_inputs directory
|
||
--setup_dir SETUP_DIR
|
||
Local path to the setup directory
|
||
--inputs_dir INPUTS_DIR
|
||
Local path to the inputs directory
|
||
--container_names ContainerType=str [ContainerType=str ...]
|
||
custom container names (eg: setup=my-setup-container)
|
||
```
|
||
|
||
## From the SDK
|
||
|
||
From the perspective of the SDK, this looks _very_ similar to the existing templates. All of the arguments from the request are converted into named arguments with appropriate type signatures.
|
||
|
||
```
|
||
❯ python
|
||
Python 3.8.2 (default, Jul 16 2020, 14:00:26)
|
||
[GCC 9.3.0] on linux
|
||
Type "help", "copyright", "credits" or "license" for more information.
|
||
>>> from onefuzz.api import Onefuzz
|
||
>>> a = Onefuzz()
|
||
>>> help(a.job_templates.submit.libfuzzer)
|
||
Help on method func in module onefuzz.job_templates.main:
|
||
|
||
func(platform: onefuzztypes.enums.OS, *, project: str, name: str, build: str, pool_name: str, target_exe: <function NewType.<locals>.new_type at 0x7f7909338040> = 'fuzz.exe', duration: int = 24, target_workers: Union[int, NoneType], vm_count: int = 2, target_options: Union[List[str], NoneType], target_env: Union[Dict[str, str], NoneType], reboot_after_setup: bool = False, check_retry_count: Union[int, NoneType], target_timeout: Union[int, NoneType], tags: Union[Dict[str, str], NoneType], readonly_inputs_dir: Union[Directory, NoneType], setup_dir: Union[Directory, NoneType], inputs_dir: Union[Directory, NoneType], container_names: Union[Dict[onefuzztypes.enums.ContainerType, str], NoneType] = None) -> onefuzztypes.models.Job method of onefuzz.job_templates.main.TemplateHandler instance
|
||
Launch 'libfuzzer' job
|
||
|
||
:param Platform platform: Specify the OS to use in the job.
|
||
:param str project: Name of the Project
|
||
:param str name: Name of the Target
|
||
:param str build: Name of the Target
|
||
:param str pool_name: Execute the task on the specified pool
|
||
:param str target_exe: Path to the target executable
|
||
:param int duration: Number of hours to execute the task
|
||
:param int target_workers: Number of instances of the libfuzzer target on each VM
|
||
:param int vm_count: Number of VMs to use for fuzzing
|
||
:param list target_options: Command line options for the target
|
||
:param dict target_env: Environment variables for the target
|
||
:param bool reboot_after_setup: After executing the setup script, reboot the VM
|
||
:param int check_retry_count: Number of times to retry a crash to verify reproducability
|
||
:param int target_timeout: Number of seconds to timeout during reproduction
|
||
:param dict tags: User provided metadata for the tasks
|
||
:param Directory readonly_inputs_dir: Local path to the readonly_inputs directory
|
||
:param Directory setup_dir: Local path to the setup directory
|
||
:param Directory inputs_dir: Local path to the inputs directory
|
||
:param dict container_names: custom container names (eg: setup=my-setup-container)
|
||
```
|