# ワークフローのマニフェスト

ワークフローは YAML フォーマットで記述された **マニフェスト（Manifest）** により定義されます:

![](/docs/ja/_md-assets/0e83c08b19-workflow.png)

このマニフェストを記述し、Shisho Cloud 上のワークフローを作成することで、監査・検査やその後の対応をコードとして定義できます。

## 大構造

ワークフローのマニフェストは、以下の 5 つから構成されます:

- `version`: 定数 (`0.1.0`)
- `id`: ワークフローに対する一意な ID
- `name`: ワークフローのわかりやすい表示名
- `triggers`: ワークフローの実行タイミングの設定
- `jobs`: ワークフロー内で行う監査・監査やその後の対応の設定

つまり、ワークフローのマニフェストは、以下のようなフォーマットに従う YAML ファイルだということです:

```yaml
version: 0.1.0
id: "example-workflow"
name: "Example"

triggers:
  # ...

jobs:
  # ....
```

## トリガー

ワークフローは複数の **トリガー** を `triggers` ブロックとして定義します:

```yaml
version: 0.1.0
id: "example-workflow"

# 複数のトリガーの定義
triggers:
  # ...
```

1 つ 1 つのトリガーは、**そのワークフローがいつ実行されるべきか**を定義するものです。「一定時間おきにトリガーを実行する」といった時間に基づく定義や、「他サービスからの Webhook メッセージを受け取ったら実行する」というようなイベントに基づく定義が利用できます。

より具体的には、以下のようなトリガーを利用できます:

- `schedule` トリガー
- `github` トリガー

### `schedule` トリガー

`schedule` トリガーは、`cron` フォーマットを用いてワークフローの実行タイミングを定義するためのトリガーです。
`schedule` トリガーには `cron` フォーマットでの実行タイミング設定を 1 つ以上含めることができ、**少なくとも 1 つの cron 設定にマッチする時刻** にワークフローが実行されるようになります。

例えば、以下のようなトリガーを持つワークフローは **10 分おき**に実行されます:

```yaml
version: 0.1.0
id: "example-workflow"
triggers:
  schedule:
    # 10 分おきに実行
    - cron: "*/10 * * * *"
# ...
```

以下は複数の `cron` 設定を含む `schedule` トリガーの例です:

```yaml
version: 0.1.0
id: "example-workflow"
triggers:
  schedule:
    - cron: "*/10 * * * *"
    - cron: "*/30 * * * *"
# ...
```

:::note
複数の `cron` 設定が、同一時刻でのワークフローの実行を指示する場合であっても、ワークフローの実行は **少なくとも 1 回** しか保証されません。
:::

### `github` トリガー

`github` トリガーは、GitHub から Shisho Cloud に対して届けられた Webhook メッセージを用いてワークフローの実行タイミングを定義するためのトリガーです。
`github` トリガーには、Webhook メッセージに基づくワークフローの実行条件を、1 つまたは複数個設定できます。
そのような設定のもと、**GitHub から届いた Webhook メッセージが少なくとも 1 つの設定にマッチする場合** にワークフローが実行されるようになります。

例えば、以下のようなトリガーを持つワークフローは、**各リポジトリのデフォルトブランチに対する push イベント**が発生した際に実行されます:

```yaml
version: 0.1.0
id: "example-workflow"
triggers:
  github:
    - push:
        branches:
          - :default_branch
```

以下は複数の条件を含む `github` トリガーの例です:

```yaml
version: 0.1.0
id: "example-workflow"
triggers:
  github:
    - push:
        branches:
          - :default_branch
    - push:
        branches:
          - staging
```

GitHub が Webhook メッセージを通して配信するイベント情報のうち、Shisho Cloud は現在、以下のようなイベントを処理できます:

- `push` イベント ([GitHub によるドキュメント](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#push))
- `pull_request` イベント ([GitHub によるドキュメント](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request))

#### `push` イベントの処理

push イベントに基づきワークフローの実行を制御する場合、以下のようなフィルタをご利用いただけます:

- `organization`: push 先のリポジトリが所属する GitHub Organization 名が、指定した文字列と一致する
- `name`: push 先のリポジトリ名が、指定した文字列と一致する
- `push.branches`: 指定された **パターン** のうち、少なくとも 1 つに push 先のブランチ名が一致する
  - パターンと push 先ブランチ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる
    - `*`: `/` を除く 1 文字以上の文字列にマッチ
    - `**`: 1 文字以上の文字列にマッチ
    - 3 つ以上 `*` が続く場合:
      - `*` が奇数個: 最後が `/` でない 1 文字以上の文字列
      - `*` が偶数個: 1 文字以上の文字列にマッチ
  - パターンが以下のどれかに完全一致する場合、一致の検証は特別な挙動をとる
    - `:default_branch`: パターンと push 先ブランチ名を比較する代わりに、当該リポジトリのデフォルトブランチ名と push 先ブランチ名を比較する
- `push.branches_ignore`: 指定された **パターン** のどれとも push 先のブランチ名が一致しない
  - パターン中では `branches` 同等の特殊文字・特殊な識別子が利用可能
- `push.tags`: 指定された **パターン** のうち少なくとも 1 つに push されたタグ名が一致する
  - パターンと push されたタグ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる
    - `*`: `/` を除く 1 文字以上の文字列にマッチ
    - `**`: 1 文字以上の文字列にマッチ
    - 3 つ以上 `*` が続く場合:
      - `*` が奇数個: 最後が `/` でない 1 文字以上の文字列
      - `*` が偶数個: 1 文字以上の文字列にマッチ
- `push.tags_ignore`: 指定された **パターン** のどれとも push されたタグ名が一致しない
  - パターン中では `tags` 同等の特殊文字が利用可能

上述のフィルタが複数指定された場合は、それらは AND 条件として扱われます。つまり、全てのフィルタにマッチする場合のみ、ワークフローが実行されるようになります。

以下は上述のフィルタの利用例です:

```yaml
triggers:
  github:
    # GitHub からの push イベントに基づきワークフローを実行する。ただし:
    # - octcat/Hello-World に対する push イベントのみに反応する
    # - staging ブランチ、または feat/ から始まるブランチにのみ反応する
    - organization: "octcat"
      name: "Hello-World"
      push:
        branches:
          - staging
          - feat/**
```

#### `pull_request` イベントの処理

`pull_request` イベントに基づきワークフローの実行を制御する場合、以下のようなフィルタをご利用いただけます:

- `organization`: push 先のリポジトリが所属する GitHub Organization 名が、指定した文字列と一致する
- `name`: push 先のリポジトリ名が、指定した文字列と一致する
- `pull_request.types`: `pull_request` イベントの種別が、指定したリストの中に含まれる
- `pull_request.branches`: 指定された **パターン** のうち、少なくとも 1 つに `pull_request` の**ベースブランチ名**が一致する
  - パターンと `pull_request` の ベースブランチ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる
    - `*`: `/` を除く 1 文字以上の文字列にマッチ
    - `**`: 1 文字以上の文字列にマッチ
  - パターンが以下のどれかに完全一致する場合、一致の検証は特別な挙動をとる
    - `:default_branch`: パターンと `pull_request` の**ベースブランチ名**を比較する代わりに、当該リポジトリのデフォルトブランチ名と `pull_request` の**ベースブランチ名**を比較する
- `PULL_REQUEST.branches_ignore`: 指定された **パターン** のどれとも `pull_request` の**ベースブランチ名**が一致しない
  - パターン中では `branches` 同等の特殊文字・特殊な識別子が利用可能

上述のフィルタが複数指定された場合は、それらは AND 条件として扱われます。つまり、全てのフィルタにマッチする場合のみ、ワークフローが実行されるようになります。

以下は上述のフィルタの利用例です:

```yaml
triggers:
  github:
    # GitHub からの pull_request イベントに基づきワークフローを実行する。ただし:
    # - octcat/Hello-World に対する pull_request イベントのみに反応する
    # - synchronize, opened, reopened イベントにのみ反応する
    - organization: "octcat"
      name: "Hello-World"
      pull_request:
        types:
          - synchronize
          - opened
          - reopened
```

## ジョブ

ワークフローは複数の **ジョブ（Job）** を定義します:

```yaml
version: 0.1.0
id: "example-workflow"

# 複数のジョブの定義
jobs:
  # ....
```

一つ一つのジョブは以下を含んでいます:

- `id`: ジョブのワークフロー内で一意な ID
- `name`: ジョブのわかりやすい表示名
- `decide`: どんなデータを・どのように検査/監査するか
- `notify` (optional): ジョブ実行の各フェーズにおいて、どのように通知するか

### `decide` ブロック

どんなデータを・どのように検査/監査するかを定義するために、ジョブは必ず一つの **`decide` ブロック**を含みます:

```yaml
version: 0.1.0
id: "example-workflow"

jobs:
  - id: "..."
    name: "..."

    # decide ブロック
    decide:
      # ...
```

#### 構造

「どんなデータを・どのように検査/監査するか」のうち、「どんなデータを」の部分を定義するものとして、`decide` ブロックには必ず以下が含まれます:

- `input` ブロック

「どのように検査/監査するか」の部分を定義するものとしては、を必ず以下の少なくとも一方が含まれます:

- `rego` ブロック
- `uses` ブロック

図示すると、検査/監査の対象（画像左部分）と、検査/監査の方法（画像中央部分）を規定するのがこれら 2 つの要素です:

![](/docs/ja/_md-assets/2aa0a73b37-decide-block.png)

#### `input` ブロック

`input` ブロックは、GraphQL クエリの形で、検査/監査対象のデータを定義するものです。
Shisho Cloud は、このようなクエリの定義に従って各ジョブごとにデータを取得し、その取得したデータをポリシーコードに入力します。

```yaml
jobs:
  - decide:
      input:
        # GraphQL クエリ
        schema: |
          query {
            ...
          }
```

GraphQL クエリの仕様確認や記述のためには、[簡易的な Playground](https://cloud.shisho.dev/*/playground/query) が利用できます。

以下に「連携済みの GitHub Organization やリポジトリ群」を取得するための GraphQL クエリの例を示します:

```graphql
query {
  github {
    organizations {
      login
      metadata {
        id
      }

      repositories {
        metadata {
          id
        }
        name
      }
    }
  }
}
```

#### `rego` ブロック

`rego` ブロックはポリシー言語 [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) を用いて、「どのように検査/監査するか」を規定するためのものです。
ポリシーは、ジョブ中の GraphQL クエリの定義通りに Shisho Cloud により取得されたデータを受け取ります。
そして、複数の **Decision** と呼ばれるデータを出力することで、Shisho Cloud に対して検査/監査の結果を伝達します。
Shisho Cloud は、この伝達されたデータを元に、セキュリティ状況の可視化や改善をサポートします。

より具体的には、Shisho Cloud と Rego で書かれたポリシーは、以下のような形で値のやり取りします:

- ポリシーへの入力: Rego 上の変数 `input` 経由で与えられる
- ポリシーからの出力: Rego 上の変数 `decisions` 経由で行う

##### GraphQL クエリに従うデータのポリシーへの入力

Shisho Cloud は、ジョブ中の `input` ブロックで記述された GraphQL クエリを用いてデータを取得し、それをポリシーに入力します。取得されたデータは、`input` **変数** からアクセスできるようになっています。

例えば GitHub 組織の一覧を取得し、それに対して検査/監査を実施するために、ジョブ中の `input` ブロックで、以下のようなクエリを記述したとします:

```graphql
query {
  github {
    organizations {
      login
      requiresTwoFactorAuthentication
    }
  }
}
```

このとき、Rego ポリシー中からアクセスできる `input` という変数に、以下のようなオブジェクトが格納されます:

```json
{
  "github": {
    "organizations": [
      {
        "login": "octcat",
        "requiresTwoFactorAuthentication": true
      },
      {
        "login": "your-org-name",
        "requiresTwoFactorAuthentication": false
      }
    ]
  }
}
```

そのため、以下のような記述を Rego ポリシー中で行うことにより、GraphQL クエリを用いて取得した GitHub 組織の一覧が取得できます:

```rego
input.github.organizations
```

もし各 GitHub 組織に関する `requiresTwoFactorAuthentication` の値（組織中のメンバーに二要素認証の使用を強制しているかを表す bool 値）を検証したい場合は、以下のように記述できます:

```rego
org := input.github.organizations[_]
allowed := org.requiresTwoFactorAuthentication == true
```

:::info
[`shishoctl`](/docs/ja/c/accessing-via-shishoctl-cli/index.md) コマンドを利用すると、記述した GraphQL クエリを元にポリシーに対するテスト入力を作成できます。
同コマンドは、Rego ポリシーを [Open Policy Agent (OPA)](https://www.openpolicyagent.org/) コマンドでテストする場合に利用できます。

以下に `jobs[].decide.rego` ポリシーに対する入力として利用できる JSON 形式のテストデータを、GraphQL クエリから生成するコマンドの例を示します:

```sh
shishoctl testcase generate \
  --type decision \
  --org (Shisho Cloud 組織の ID) \
  --query-path (GraphQL クエリが格納されたファイルへのパス)
```

ポリシー記述に Rego 以外を利用している場合や、ポリシーの利用箇所が `jobs[].notify` 以下である場合も、上記のコマンドのオプションを変更することで対応できます。
詳しくは `shishoctl testcase generate --help` コマンドの出力を確認してください。
:::

##### ポリシーからの結果の出力

Rego 中で `decisions` 変数に値をセットすることで、検査/監査の結果見つかった問題や、あるいは問題が見つからなかったという事実を記録しておくことができます。別の言い方をすると、Shisho Cloud は、Rego ポリシー中の `decisions` 変数の値を加工の上記録します。

```rego
decisions := [
  ...
]

decisions[n] {
  n := ...
}
```

ここで、ポリシーから `decisions` 変数に値をセットする際、その値は [**Decision オブジェクト**](/docs/ja/g/api/objects/decision.md) である必要があります。**Decision オブジェクト** とは、**どのような対象に対する・どのような種類の・どのような結果か** を表すデータオブジェクトで、通常 [Rego SDK](https://github.com/flatt-security/shisho-cloud-rego-libraries/tree/main/decision) を用いて生成されるものです。以下に例を示します:

```rego
decisions[d] {
	allowed := org.requiresTwoFactorAuthentication == true

	d := shisho.decision.github.org_2fa_status({
    # whether the target is allowed by this policy or not
		"allowed": allowed,
    # the target of the decision (e.g. a GitHub repository, etc.)
		"subject": org.metadata.id,
    # clues on this decision
		"payload": shisho.decision.github.org_2fa_status_payload({"enabled": org.requiresTwoFactorAuthentication}),
	})
}
```

#### `decide` ブロックの例

以下のジョブ定義の例では、検査/監査対象のデータを定義する `input` ブロックと、その検査/監査方法を定義する `rego` ブロックを定義しています:

```yaml
jobs:
  - name: check-with-foobar
    id: check-with-foobar
    decide:
      rego: |
        import data.shisho

        decisions[d] {
          org := input.github.organizations[_]
          allowed := org.requiresTwoFactorAuthentication == true

          d := shisho.decision.github.org_2fa_status({
            "allowed": allowed,
            "subject": org.metadata.id,
            "payload": shisho.decision.github.org_2fa_status_payload({"enabled": org.requiresTwoFactorAuthentication}),
          })
        }
      input:
        schema: |
          query {
            github {
              organizations {
                requiresTwoFactorAuthentication
                metadata {
                  id
                }
              }
            }
          }
```

#### `with` ブロック

`with` ブロックは、ポリシーコードへ渡すパラメータなどを定義するためのものです。この機能を用いることで、より柔軟で効率的なポリシーコードを作成することができます。

定義されたパラメータは **Rego / TypeScript** 両方のポリシーコード内で使用することが可能です。

以下の `with` ブロックの定義の例では、ポリシーコード内で使用するシンボル名、パラメータタイプ、説明および値を定義しています。

```yaml
jobs:
  - decide:
      decide: ...
      input: ...
      with:
        allowName:
          type: string
          description: skip specific instances
          value: "test-sample"
```

以下に **Rego / TypeScript** のポリシーコード内でパラメータを使用する例を示します。

```rego
# example: Rego policy code
...
allow_specific_instance(instance) {
	instance.machineType == "f1-micro"
} else = contains(instance.name, input.params.allowName)
...
```

```typescript
// example: TypeScript policy code
...
if (instance.name.includes(params.allowName)) {
    return {
      ...
    }
}
...
```

`with` ブロックが定義されている場合、Shisho Cloud のワークフローエディタではパラメータ設定 UI が表示されます。

![](/docs/ja/_md-assets/fd6fc3f1e0-with-block-example.png)

:::info
`パラメータタイプ` は以下の4種類があり、それぞれの種類によってパラメータ設定 UI の表示が変わります。

- **string**
- **notification_target**
- **slack_channel**
- **resource_exception**

ポリシーコードにパラメータを渡す場合は `string` を選択してください。
他のタイプに関しては [**こちらのページ**](/docs/ja/g/getting-started/optimize-your-workflows/parameter.md) も参照してください。
:::

### `notify` ブロック

ジョブ実行の各フェーズにおいて、どのように通知するかを定義するために、ジョブは **`notify` ブロック**を含むことがあります:

```yaml
version: 0.1.0
id: "example-workflow"

jobs:
  - id: "..."
    name: "..."

    # notify ブロック
    notify:
      # 入力の定義
      input: # ...

      # 通知方法の定義
      rego: # ....
      uses: # ....
```

#### 構造

`notify` ブロックは **「通知すべきかの判定や、通知内容の構成のために必要なデータ」** を規定する、以下のブロックを必ず含みます:

- `input` ブロック

また、**通知すべきかの判定方法や、通知内容の構成の方法** を規定する、以下のブロックを必ず含みます:

- `rego` ブロック
- `uses` ブロック

図示すると、どのような通知をするかの判断に必要な補助データ（画像左部分）と、通知ロジック（画像中央部分）を規定するのがこれら 2 つの要素です:

![](/docs/ja/_md-assets/b7175a123e-notify-block.png)

#### `input` ブロック

`notify` ブロック中の `input` ブロックは、`decide` ブロック中の `input` ブロックと同様に振る舞います。
詳細は `decide` ブロック中の `input` ブロックの説明をご確認ください。

:::info
[`shishoctl`](/docs/ja/c/accessing-via-shishoctl-cli/index.md) コマンドを利用すると、記述した GraphQL クエリを元にポリシーに対するテスト入力を作成できます。
同コマンドは、Rego ポリシーを [Open Policy Agent (OPA)](https://www.openpolicyagent.org/) コマンドでテストする場合に利用できます。

以下に `jobs[].notify.rego` ポリシーに対する入力として利用できる JSON 形式のテストデータを、GraphQL クエリから生成するコマンドの例を示します:

```sh
shishoctl testcase generate \
  --type notification \
  --org (Shisho Cloud 組織の ID) \
  --query-path (GraphQL クエリが格納されたファイルへのパス)
```

ポリシー記述に Rego 以外を利用している場合や、ポリシーの利用箇所が `jobs[].decide` 以下である場合も、上記のコマンドのオプションを変更することで対応できます。
詳しくは `shishoctl testcase generate --help` コマンドの出力を確認してください。
:::

#### `rego` ブロック

`rego` ブロックはポリシー言語 [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) を用いて、「通知すべきかの判定方法や通知内容の構成の方法」を規定するためのものです。

##### GraphQL クエリに従うデータのポリシーへの入力

Shisho Cloud は、ジョブ中の `input` ブロックで記述された GraphQL クエリを用いてデータを取得し、それをポリシーに入力します。取得されたデータは、`input.query` **変数** からアクセスできるようになっています。

:::info
`input` 変数からは、ジョブの実行状態など、ワークフロー機能に関連する様々な値が利用できます。詳しくは [Rego インラインポリシーが従う API についての資料](/docs/ja/g/api/rego.md) を参照してください。
:::

##### ポリシーからの結果の出力

Rego ポリシー中で `notifications` 変数に値をセットすることで、Shisho Cloud に通知の送信を指示することが出来ます。

```rego
notifications := [
  ...
]

notifications[n] {
  n := ...
}
```

ここで、ポリシーから `notifications` 変数に値をセットする際、その値は [Notification オブジェクト](/docs/ja/g/api/objects/notification.md) の配列である必要があります。
ここで、**Notification オブジェクト** は、**どのような対象に対する・どのような種類の・どのような結果か** を表すデータオブジェクトで、通常 [Rego SDK](https://github.com/flatt-security/shisho-cloud-rego-libraries/tree/main/notification) を用いて生成されるものです。以下に例を示します:

```rego
notifications[n] {
  target := shisho.notification.to_notification_group("id-of-notification-group")
  msg := "test"

  n := shisho.notification.new(target, msg)
}

notifications[n] {
  target := shisho.notification.to_github_issue(
      #...
  )
  msg := "test"

  n := shisho.notification.new(target, msg)
}
```

#### `notify` ブロックの例

以下のジョブ定義の例では、検査/監査対象のデータを定義する `input` ブロックと、その検査/監査方法を定義する `rego` ブロックを定義しています:

```yaml
jobs:
  - name: check-with-foobar
    id: check-with-foobar
    decide:
      # ...
    notify:
      input:
        schema: |
          query {
            __typename
          }
      rego: |
        package policy.github.config.notify

        import data.shisho

        notification_groups := []

        notifications[n] {
          input.running_state == shisho.job.running_state_in_queue

          ng := notification_groups[_]
          target := shisho.notification.to_notification_group(ng)

          msg := concat("", ["Shisho Cloud has started to audit the GitHub configurations! See https://cloud.shisho.dev for further information."])

          n := shisho.notification.new(target, msg)
        }
```
