# 테스트

## 개요

디택션을 테스트하면 배포 후 디택션이 예상대로 동작하고 생성할 수 있음을 보장합니다 [signals](https://docs.panther.com/ko/detections/signals) 적절하게. 테스트는 또한 신뢰성을 높이고 시간이 지나며 코드가 발전함에 따라 회귀를 방지합니다.

테스트는 특정 디택션에 대한 테스트 로그 이벤트를 정의하고, 해당 테스트 이벤트가 그 디택션에 의해 처리될 때 알러트가 생성될 것으로 예상하는지 여부를 표시하는 방식으로 작동합니다. Panther는 각 디택션에 대해 필수 최소 테스트 수를 강제하지 않지만, 최소 두 개(거짓 양성 하나와 참 양성 하나)를 구성하는 것이 권장됩니다.

사용자 지정 디택션(즉, 다음이 아닌 디택션)에 대해서는 테스트를 생성, 편집 및 삭제할 수 있습니다 [Panther 관리형](https://docs.panther.com/ko/detections/panther-managed)). 다음에 대한 테스트는 [Panther 관리형](https://docs.panther.com/ko/detections/panther-managed) 디택션은 Panther에서 관리하며 읽기 전용입니다.

Panther의 [데이터 리플레이](https://docs.panther.com/ko/detections/testing/data-replay), 이를 사용하면 과거 로그 데이터를 하나의 룰에 통과시켜 결과를 미리 볼 수 있어 테스트 중에도 유용합니다.

## 테스트 사용

### 테스트 생성 방법

다음이 아닌 디택션에 대한 테스트를 생성할 수 있습니다 [Panther 관리형](https://docs.panther.com/ko/detections/panther-managed) Panther Console에서, 또는 다음을 사용하는 CLI 워크플로에서 [Panther Analysis Tool](https://docs.panther.com/ko/panther/detections-repo/pat) (PAT).

{% tabs %}
{% tab title="콘솔: 디택션 페이지" %}

1. Panther Console의 왼쪽 탐색 모음에서 다음을 클릭하세요. **Detections**.
2. 기존 디택션의 이름을 클릭하거나 [새 디택션 생성](https://docs.panther.com/ko/detections/..#how-to-write-detections).
3. 아래로 스크롤하여 **테스트** 섹션.
4. 오른쪽의 **단위 테스트** 타일에서 다음을 클릭합니다 **새로 추가**.\
   ![The Unit Test section of the detection page shows the names of one already-created test ("Failed login"). In the upper right corner there's a "Add New" button, which is circled.](https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-1c8e182a8356a09eaa897df0216cf44cd0d98367%2Fimage.png?alt=media)
   * 디택션이 다음인 경우 [Panther 관리형](https://docs.panther.com/ko/detections/panther-managed), **새로 추가** 새 테스트를 생성할 수 없으므로 비활성화됩니다.
5. 새로 생성된 테스트에는 임시 이름이 지정됩니다. 그 오른쪽에서 점 세 개 아이콘을 클릭하고 다음을 선택합니다 **이름 바꾸기**.\
   ![In the Unit Test section, a test with the placeholder name "Test-b789c4" is shown. The three dot icon to its right has been selected, which opened a menu with two options: Rename and Delete.](https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-1ba8179d6e92948f29fb30932d43d52738997979%2Fimage.png?alt=media)
6. 테스트에 의미 있는 이름을 입력하고 저장하려면 Enter 또는 Return 키를 누릅니다.
7. 다음을 설정합니다 **디택션은 예시 이벤트를 기반으로 트리거되어야 합니다** 토글을`예` 또는 `아니요`.
8. 텍스트 편집기에서 테스트 JSON 이벤트를 작성합니다.
9. 테스트 통과 여부를 보려면 다음을 클릭합니다 **테스트 실행**.
10. 완료되면 페이지 오른쪽 상단에서 다음을 클릭합니다 **Update** 또는 **저장**.
    {% endtab %}

{% tab title="콘솔: 알러트 이벤트" %}
Panther Console에서는 알러트와 연결된 실제 이벤트를 사용하여 알러트에서 직접 새 단위 테스트를 생성할 수 있습니다. 다음의 지침을 따르세요 [알러트 이벤트에서 필터 추가](https://docs.panther.com/ko/rules/inline-filters#add-filters-from-an-alert-event), 9단계와 10단계를 유의해서 확인하세요.

이 기능은 룰에 대해서만 사용할 수 있습니다.
{% endtab %}

{% tab title="CLI" %}
**CLI 워크플로에서 테스트 생성 방법**

**룰 및 예약된 룰**

YAML 파일에서(둘 다 [Python](https://docs.panther.com/ko/detections/rules/python) 및 [간단한 디택션](https://docs.panther.com/ko/detections/rules/writing-simple-detections)) 다음을 추가합니다 `Tests` 키와 다음 필드들:

```yaml
Tests:
  -
    Name: 첫 번째 테스트를 설명하는 이름
    LogType: LogType.GoesHere
    ExpectedResult: true 또는 false
    Log:
      {
        "hostName": "test-01.prod.acme.io",
        "user": "martin_smith",
        "eventTime": "June 22 5:50:52 PM"
      }
```

**정책**

YAML 파일에 다음을 추가합니다 `Tests` 키. 다음의 값은 `Resource` 에서 직접 복사한 JSON 객체일 수 있습니다 **정책** > **리소스** 탐색기.

```yaml
Tests:
  -
    Name: 첫 번째 테스트를 설명하는 이름.
    ResourceType: AWS.S3.Bucket
    ExpectedResult: true
    Resource:
      {
        "PublicAccessBlockConfiguration": null,
        "Region": "us-east-1",
        "Policy": null,
        "AccountId": "123456789012",
        "LoggingPolicy": {
          "TargetBucket": "access-logs-us-east-1-100",
          "TargetGrants": null,
          "TargetPrefix": "acmecorp-fiancial-data/"
        },
        "EncryptionRules": [
          {
            "ApplyServerSideEncryptionByDefault": {
              "SSEAlgorithm": "AES256",
              "KMSMasterKeyID": null
            }
          }
        ],
        "Arn": "arn:aws:s3:::acmecorp-fiancial-data",
        "Name": "acmecorp-fiancial-data",
        "LifecycleRules": null,
        "ResourceType": "AWS.S3.Bucket",
        "Grants": [
          {
            "Permission": "FULL_CONTROL",
            "Grantee": {
              "URI": null,
              "EmailAddress": null,
              "DisplayName": "admins",
              "Type": "CanonicalUser",
              "ID": "013ae1034i130431431"
            }
          }
        ],
        "Versioning": "Enabled",
        "ResourceId": "arn:aws:s3:::acmecorp-fiancial-data",
        "Tags": {
          "aws:cloudformation:logical-id": "FinancialDataBucket"
        },
        "Owner": {
          "ID": "013ae1034i130431431",
          "DisplayName": "admins"
        },
        "TimeCreated": "2020-06-13T17:16:36.000Z",
        "ObjectLockConfiguration": null,
        "MFADelete": null
      }
```

{% endtab %}
{% endtabs %}

### Panther Console에서 테스트 이름 변경 또는 삭제 방법

다음이 아닌 디택션에 대한 테스트는 이름을 변경하거나 삭제할 수 있습니다 [Panther 관리형](https://docs.panther.com/ko/detections/panther-managed).

1. Panther Console의 왼쪽 탐색 모음에서 다음을 클릭하세요. **Detections**.
2. 디택션의 이름을 클릭합니다.
3. 아래로 스크롤하여 **테스트** 섹션.
4. 다음 내부의 **단위 테스트** 타일에서 이름을 변경하거나 삭제하려는 테스트를 찾습니다.
5. 테스트 이름 오른쪽에서 점 세 개 아이콘을 클릭합니다.

   <figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-e5f2fcc719b42d08bc1c97d793dd223af495b208%2Fimage%20(2).png?alt=media" alt="In the Unit Test section, a test named &#x22;Reset by Company Admin&#x22; is shown. The three dot icon to its right has been selected, which has opened a menu with two options: Rename and Delete."><figcaption></figcaption></figure>

   * 디택션이 다음인 경우 [Panther 관리형](https://docs.panther.com/ko/detections/panther-managed), 해당 테스트는 이름을 변경하거나 삭제할 수 없습니다. 대신 각 테스트 이름 옆의 점 세 개 아이콘 대신 Panther 아이콘이 표시됩니다.

     <figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-66c6c4fcddcad8ff9db3594c8cf025499ab576eb%2Fimage.png?alt=media" alt="The Unit Test section shows multiple test names, each with a Panther icon to its left."><figcaption></figcaption></figure>
6. 다음을 클릭하세요. **이름 바꾸기** 또는 **Delete**.
   * 이름을 변경하는 경우 새 이름을 입력하고 저장하려면 Enter 또는 Return 키를 누릅니다.
   * 삭제하는 경우 **테스트 삭제** 확인 모달이 팝업됩니다.
     * 다음을 선택합니다 **Confirm**.

### 테스트 예시

* 다음을 클릭하세요. **편집** 페이지 오른쪽 상단에서.
* 아래로 스크롤하여 **룰 함수** 텍스트 편집기 아래의 **단위 테스트** 텍스트 편집기로 이동합니다.

이전 예시를 유지하여(다음에서 [룰](https://docs.panther.com/ko/detections/rules)) 이 디택션에 대해 두 개의 테스트를 작성해 보겠습니다:

{% tabs %}
{% tab title="Python" %}

```python
def 룰(event):
  return event.get('status') == 200 and 'admin-panel' in event.get('request')

    
def title(event):
  return f"{event.get('remoteAddr')}에서 성공적인 관리자 패널 로그인 디택션됨"


def dedup(event):
  return event.get('remoteAddr')
```

{% endtab %}

{% tab title="간단한 디택션" %}

```yaml
디택션:
  - KeyPath: status
    Condition: Equals
    Value: 200
  - KeyPath: request
    Condition: Contains
    Value: 'admin-panel'

AlertTitle: "{remoteAddr}에서 성공적인 관리자 패널 로그인 디택션됨"

GroupBy:
  - KeyPath: remoteAddr
```

{% endtab %}
{% endtabs %}

`이름`: 성공적인 admin-panel 로그인

`테스트 이벤트가 알러트를 트리거해야 함`: 예

```javascript
{
  "httpReferer": "https://domain1.com/?p=1",
  "httpUserAgent": "Chrome/80.0.3987.132 Safari/537.36",
  "remoteAddr": "180.76.15.143",
  "request": "GET /admin-panel/ HTTP/1.1",
  "status": 200,
  "time": "2019-02-06 00:00:38 +0000 UTC"
}
```

`이름`: access-policy 페이지에 대한 오류 요청

`테스트 이벤트가 알러트를 트리거해야 함`: 아니요

```javascript
{
  "httpReferer": "https://domain1.com/?p=1",
  "httpUserAgent": "Chrome/80.0.3987.132 Safari/537.36",
  "remoteAddr": "180.76.15.143",
  "request": "GET /access-policy/ HTTP/1.1",
  "status": 500,
  "time": "2019-02-06 00:00:38 +0000 UTC"
}
```

{% hint style="info" %}
디택션의 최고 신뢰성을 보장하기 위해 원하는 만큼 다양한 조합을 사용하세요.
{% endhint %}

## 모의(Mock)

{% hint style="warning" %}
Panther의 디택션 내에서 외부 API 요청을 수행하는 것은 강력히 권장되지 않습니다. 일반적으로 디택션은 매우 큰 규모로 처리되며, API 요청을 수행하면 수신 시스템에 과부하를 주고 룰이 다음을 초과하게 만들 수 있습니다 [15초 런타임 제한](https://docs.panther.com/ko/rules#rule-errors-and-scheduled-rule-errors).
{% endhint %}

Panther의 테스트 프레임워크는 Python 함수 호출에 대한 기본적인 모의 기능을 제공합니다.

외부 API 호출을 수행하는 디택션에 대한 단위 테스트를 작성할 때, 모의를 사용하면 실제로 API 호출을 실행하지 않고도 테스트에서 서버 응답을 모방할 수 있습니다.

모의는 다음으로 정의됩니다 **Mock Name** 및 **Return Value** (또는 `objectName` 및 `returnValue`, CLI 워크플로에서는), 이는 각각 모의할 함수의 이름과 반환할 모의 값(반드시 다음이어야 함)을 나타냅니다 `문자열`).

모의는 단위 테스트 수준에서 정의되므로 각 단위 테스트마다 서로 다른 모의를 정의할 수 있습니다.

### 모의 사용 방법

Console 및 CLI 워크플로 모두에서 테스트용 모의를 구성할 수 있습니다.

모의는 오직 다음만 반환할 수 있습니다 `문자열` 값만 가능하며, 모의되는 함수가 다른 데이터 유형을 반환하더라도 마찬가지입니다. 이는 모의 기능의 알려진 제한 사항입니다.

{% hint style="info" %}
모의 반환값은 반드시 다음 유형이어야 한다는 제한 때문에 `문자열`, 문자열이 아닌 데이터 유형을 반환하는 함수를 모의하는 경우 테스트가 모의 값을 성공적으로 사용할 수 있도록 함수의 반환값을 원래 데이터 유형으로 캐스팅하는 것이 권장됩니다.

예를 들어, 다음의 `룰()` 함수 `get_counter()` 가 정수를 반환한다면 다음을 사용하세요 `int(get_counter())`.
{% endhint %}

{% tabs %}
{% tab title="Console" %}
Console에서 단위 테스트에 모의를 추가하려면:

1. 다음 내부의 **단위 테스트** 타일에서 다음을 찾습니다 **Mock Testing** 섹션, 테스트 이벤트 편집기 아래.
2. 다음 값들을 추가합니다 **Mock Name** 및 **Return Value**.
3. 모의가 예상대로 동작하는지 테스트하려면 다음을 클릭합니다 **테스트 실행**.

<figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-5c62f069299bac683b60084418208dc84767860e%2Fimage%20(4).png?alt=media" alt="In the Panther Console, the Unit Test section of a detection page is shown. Below the test event, there is a Mock Testing section. It contains fields for Mock Name and Return Value."><figcaption></figcaption></figure>
{% endtab %}

{% tab title="CLI" %}
CLI 워크플로에서 단위 테스트에 모의를 추가하려면:

테스트 케이스에 다음을 추가합니다 `모의(Mock)` 키. 이 키는 모의하려는 함수 목록과 그 모의 반환값을 정의합니다. 하나의 테스트에서 여러 함수를 모의할 수 있습니다.

다음 값은 유의하세요 `returnValue` 반드시 다음이어야 합니다 `문자열`, 디택션 코드의 실제 함수가 다른 데이터 유형을 반환하더라도 마찬가지입니다. 이는 다음에 의해 검증됩니다 [`pat test`](https://docs.panther.com/ko/panther/detections-repo/pat/pat-commands#test-running-unit-tests).

예를 들어, 함수 `get_counter` 를 항상 반환하도록 모의하고 싶고 `"1"` 그리고 함수 `geoinfo_from_ip` 가 항상 특정 geo IP 정보 집합을 반환하도록 하고 싶다면, 다음과 같이 단위 테스트를 작성할 수 있습니다:

```yaml
Tests:
  -
    Name: 모의가 있는 테스트
    LogType: LogType.Custom
    ExpectedResult: true
    Mocks:
      - objectName: get_counter
        returnValue: "1"
      - objectName: geoinfo_from_ip
        returnValue: |
          {
            "region": "UnitTestRegion",
            "city": "UnitTestCityNew",
            "country": "UnitTestCountry"
          }
    Log:
      {
        "hostName": "test-01.prod.acme.io",
        "user": "martin_smith",
        "eventTime": "June 22 5:50:52 PM"
      }
```

모킹을 사용하면 API 키나 네트워크 액세스 없이, 그리고 CI/CD 파이프라인에서 외부 추적 시스템(예: panther KV store)의 상태를 흐트러뜨리지 않고 네트워크 호출을 에뮬레이션할 수 있습니다.

관련 문서를 참조하여 다음 사용에 대해 자세히 알아보세요: [Panther Analysis Tool (PAT)](https://docs.panther.com/ko/panther/detections-repo/pat) 및 [CI/CD 워크플로](https://docs.panther.com/ko/panther/detections-repo/ci-cd/deployment-workflows/circle-ci).
{% endtab %}
{% endtabs %}

모킹은 다음 항목에서 허용됩니다. **global** 및 **built-in** Python 네임스페이스. 여기에는 다음이 포함됩니다:

* 가져온 모듈 및 함수
* [전역 헬퍼](https://docs.panther.com/ko/detections/rules/python/globals), Panther가 관리하는 것과 커스텀인 것 모두 포함

Python은 두 가지 형태의 import를 제공하며, 다음과 같이 모킹할 수 있습니다:

* `import package`
  * 모크 이름: `package`
* `from package import module`
  * 모크 이름: `module`

### 예제 모크

이 예제는 다음을 기반으로 합니다. [AWS Config Global Resources](https://github.com/panther-labs/panther-analysis/blob/main/policies/aws_config_policies/aws_config_global_resources.py) 디택션.

이 디택션은 전역 헬퍼 함수 `resource_lookup` 를 사용하며, 이는 `panther_aws_helpers` 에서 `resources-api` 를 조회하고 리소스 속성을 반환합니다. 그러나 단위 테스트는 외부 API 호출 없이 수행할 수 있어야 합니다.

<figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-da3a3e5a9721b85b358242e0dd78ab23de582854%2Fimage%20(3).png?alt=media" alt="A Unit Test within a Detection&#x27;s details page is shown. In the Mock Testing section, the fields for Mock Name and Return Value are blank."><figcaption></figcaption></figure>

이 테스트는 일반적인 예제 데이터에 해당하는 리소스 매핑이 없기 때문에 실패합니다.

#### 디택션 살펴보기

```python
# --- Snipped ---
from panther_aws_helpers import resource_lookup
# --- Snipped ---
def policy(resource):
    # --- Snipped ---
    for recorder_name in resource.get("Recorders", []):
        recorder = resource_lookup(recorder_name)
        resource_records_global_resources = bool(
            deep_get(recorder, "RecordingGroup", "IncludeGlobalResourceTypes")
            and deep_get(recorder, "Status", "Recording")
        )
        if resource_records_global_resources:
            return True
    return False
    # --- Snipped ---
```

이 디택션은 `from panther_aws_helpers import resource_lookup` 규칙을 사용하므로 모크는 다음 대상에 대해 정의되어야 합니다. `resource_lookup` 함수.

모크는 실제 데이터를 활용하여 디택션 로직을 테스트할 수 있는 방법을 제공합니다.

**사용된 반환값**:

```
 { "AccountId": "012345678910", "Name": "Default", "RecordingGroup": { "AllSupported": true, "IncludeGlobalResourceTypes": true, "ResourceTypes": null }, "Region": "us-east-1", "ResourceId": "012345678910:us-east-1:AWS.Config.Recorder", "ResourceType": "AWS.Config.Recorder", "RoleARN": "arn:aws:iam::012345678910:role/PantherAWSConfig", "Status": { "LastErrorCode": null, "LastErrorMessage": null, "LastStartTime": "2018-10-05T22:45:01.838Z", "LastStatus": "SUCCESS", "LastStatusChangeTime": "2021-05-28T17:45:14.916Z", "LastStopTime": null, "Name": "Default", "Recording": true }, "Tags": null, "TimeCreated": null }
```

<figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-54ca099ea59981a93b11fe1b9a4323de264bb758%2Fimage%20(5).png?alt=media" alt="A Unit Test within a Detection&#x27;s details page is shown. The Mock Name field contains &#x22;resource_lookup&#x22; and the Return Value contains the value from the &#x22;Return Value Used&#x22; section in the documentation above this image."><figcaption></figcaption></figure>

이 리소스는 준수해야 하지만, 단위 테스트는 실패합니다.\
반환될 것으로 예상하지 않는 디택션은 `문자열` 모크에 작은 조정이 필요합니다.

이 단위 테스트를 예상대로 동작하게 하려면 다음 수정이 필요합니다:

```python
# --- Snipped ---
import json
# 또 다른 옵션: from ast import literal_eval 사용
# --- Snipped ---
def policy(resource):
    # --- Snipped ---
        recorder = resource_lookup(recorder_name)
        if isinstance(recorder, str):
            recorder = json.loads(recorder)
    # --- Snipped ---
```

이 수정이 추가되면 이제 실제 데이터로 디택션 로직을 테스트할 수 있습니다!

<figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-847e151c016412a33a28511c029eb5d0da9cbdac%2Fimage%20(6).png?alt=media" alt="A full Policy Function and Unit Test with Mock Testing is shown. The test is named &#x22;Global Recorders Present - None Recording Global Resources&#x22;"><figcaption></figcaption></figure>

#### CLI의 모크

단위 테스트 모킹은 디택션 작성용 CLI 기반 워크플로에서도 지원됩니다. CLI 기반 디택션에 단위 테스트 모크를 추가하는 방법에 대한 자세한 내용은 PAT 문서의 [단위 테스트 모킹](#how-to-use-mocks) 섹션을 참조하세요.

## 테스트 데이터 보강

다음을 사용하여 테스트 이벤트를 보강할 수 있습니다. `p_enrichment` Panther Console과 CLI 워크플로 모두에서.

{% hint style="info" %}
다음을 사용 중이라면 `lookup()` 함수를 디택션에서 사용한다면, 대신 다음의 지침을 따르세요. [다음을 사용하는 디택션 단위 테스트 `lookup()`](https://docs.panther.com/ko/rules/python#unit-testing-detections-that-use-lookup).
{% endhint %}

{% tabs %}
{% tab title="Console" %}
다음이 있는 경우 [조회 테이블](https://docs.panther.com/ko/enrichment/custom) 를 구성하고 이를 활용하는 디택션을 생성했다면, **테스트 데이터 보강** 를 JSON 이벤트 편집기의 오른쪽 상단에서 클릭하여 `p_enrichment` 가 올바르게 채워지는지 확인하세요.

<figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-8f8b864ba7570b151448cd87d7ff07b1a733fac7%2Fimage%20(7).png?alt=media" alt="Within the Unit Test section, the test event editor is shown. In the top right corner of the editor is a circled button that reads Enrich Test Data."><figcaption></figcaption></figure>
{% endtab %}

{% tab title="CLI" %}

* PAT의 `enrich-test-data` 명령을 사용하세요. [다음에 대해 자세히 알아보세요 `enrich-test-data` 여기](https://docs.panther.com/ko/panther/detections-repo/pat/pat-commands#enrich-test-data-enriching-test-data-with-enrichment-content).
  {% endtab %}
  {% endtabs %}
