Pattern

AST 및 Symbolic 기반 탐지

any/all

필드설명
any여러 조건 중 하나만 충족해도 될 때 사용
all나열한 모든 조건을 충족해야 할 때 사용

예제 1: method_1 또는 method_2 중 하나에 일치하는 호출을 찾습니다.

yaml
any:
  - pattern: method_1($$$)
  - pattern: method_2($$$)

예제 2:

yaml
all:
  - pattern: method_1($$$)
  - inside:
      pattern: $F(async ($REQ, $RES) => {$$$})
  - inside:
      pattern: for($IN = $S; $$$; $$$) {$$$}

not

특정 패턴을 제외하고 싶을때 사용

yaml
pattern: $REQ.META
not:
  inside:
    any:
      - pattern: $REQ.META.CONTENT_LENGTH
      - pattern: $REQ.META.CONTENT_TYPE

regex

regex 기반으로 매칭을 제공

예제: polyfill.io url 탐지

메타변수 STR에 저장한 문자열을 정규식으로 검사합니다.

yaml
pattern: |
  "$STR"
metavar:
  name: STR
  eq:
    regex: '(?:https?:)?//(?:[^/]*\.)?polyfill\.io'

아래 코드와 같이 탐지

javascript
async function loadPolyfill() {
  await import("https://polyfill.io/v3/polyfill.min.js"); // 탐지
}

async function loadPolyfillWithQuote() {
  await import('https://polyfill.io/v3/polyfill.min.js'); // 탐지
}

Inside

패턴 내 패턴을 탐지할때 사용

예제: someMethod 함수 내 target 함수 사용 탐지

yaml
inside:
  pattern: public void someMethod($$$){$$$}
pattern: target($$$)

아래 코드와 같이 탐지

java
public class Example {
    public void someMethod(String arg) {
        target("something"); // 탐지
    }
}

Metavariable

$를 prefix로 사용

Metavariable 유형 설명
$VAR 일치한 AST 노드를 VAR에 저장
$$$VARS 여러개의 AST 노드들을 탐지하고 싶을 시 사용
$$$ 여러 줄을 포함하여 탐지하고 싶을 시 사용

$$$은 아래와 같이 사용하여 multi-line 패턴을 탐지할 수 있음

yaml
pattern: |
  public void someMethod($$$) {$$$}

Metavariable로 추출한 값들에 대해 아래와 같이 패턴 매칭 또한 가능함

아래 룰은 NOVERIFY가 하드코딩된 문자열이거나 true일 경우를 탐지

yaml
pattern: $JWT.decode($TOKEN, $SECRET, $NOVERIFY, $$$)
metavar:
  name: NOVERIFY
  eq:
    any:
      - pattern: |
          "$$$_STR"
      - pattern: "true"

matcher

matcher는 자주 사용하는 민감 이름 분류를 엔진에서 제공하는 atomic matcher입니다. 여러 룰에 password|token|api_key|client_secret... 형태의 큰 regex를 반복해서 넣지 않아도 됩니다.

지원 값:

  • password
  • secret
  • pii
  • sensitive

예:

yaml
metavar:
  name: FIELD
  eq:
    matcher: secret

matcher는 이름/key 형태의 노드에서 quote를 제거하고 camelCase, snake_case, kebab-case, dotted name, 숫자 suffix를 token 단위로 나누어 검사합니다. 큰 expression 전체 텍스트를 민감 이름으로 분류하지 않으며, 실제 이메일 주소나 전화번호 같은 literal PII 값 탐지가 아니라 이름/key 분류용입니다.

regex_pair.left에서도 사용할 수 있습니다.

yaml
regex_pair:
  left:
    matcher: secret
  right: "[0-9a-zA-Z\\-_.=\\~@]{10,150}"

constantValue

constantValue는 JavaScript, TypeScript, TSX에서 로컬 상수 값까지 따라가서 중첩 룰을 검사할 때 사용합니다. 기존 pattern, metavar, resolvedName, taint 분석 동작은 변경하지 않으며, 룰에서 명시적으로 constantValue를 사용할 때만 활성화됩니다.

기본 형태:

yaml
constantValue:
  matches: unsafe-origin-value

metavar 아래에서도 동일하게 사용할 수 있습니다.

동작 방식:

  • 현재 노드에 중첩 룰을 먼저 직접 적용합니다.
  • 현재 노드가 identifier이면 가장 가까운 lexical scope의 value binding을 찾고, 해당 initializer 노드에 중첩 룰을 적용합니다.
  • const, typed const, initializer가 있는 let/var를 지원합니다. 단, 같은 scope에서 재할당되는 let/var는 상수로 취급하지 않습니다.
  • 문자열, 숫자, boolean, regex literal, literal 인자만 사용하는 new RegExp(...), 지원되는 상수 표현식으로만 구성된 array/object, 제한된 identifier alias chain을 지원합니다.
  • JavaScript, TypeScript, TSX 외 언어에서는 사용할 수 없습니다.

예제: CORS origin 값이 직접 unsafe regex이거나, 로컬 상수로 분리된 unsafe regex일 때 탐지

yaml
utils:
  unsafe-origin-value:
    any:
      - kind: regex
        regex: "[a-zA-Z0-9\\-]\\."
      - all:
          - pattern: new RegExp($PATTERN, $$$)
          - has:
              kind: string
              regex: "[a-zA-Z0-9\\-]\\."

rule:
  pattern: "cors({ origin: $ORIGIN, $$$ })"
  metavar:
    name: ORIGIN
    eq:
      constantValue:
        matches: unsafe-origin-value

아래 코드는 origin의 initializer인 regex literal을 따라가므로 탐지됩니다.

javascript
const origin = /example\./;
cors({ origin });

아래 코드는 origin이 재할당되므로 상수 값으로 취급하지 않아 탐지하지 않습니다.

javascript
let origin = /example\./;
origin = safeOrigin;
cors({ origin });

import/require로 가져온 함수나 객체가 특정 패키지인지 확인할 때는 resolvedName을 사용하고, 함수 인자나 설정 값처럼 로컬 상수 initializer를 검사해야 할 때 constantValue를 함께 사용합니다.

compare

Metavariable에 대한 계산 관련 조건 검사할 때 사용

  • Boolean
  • Number
    • Hex, Binary, Octal 지원 (예: 0x, 0b, 0o)

예제: 권한 검사

아래와 같이 BIT에 특정 range 내 포함되는지 확인할때 유용하게 사용 가능

yaml
any:
  - pattern: os.chmod($FILE, $BIT, $$$)
  - pattern: os.fchmod($FILE, $BIT, $$$)
  - pattern: os.lchmod($FILE, $BIT, $$$)
compare: ( $BIT >= 0o650 && $BIT < 0o100000 ) || $BIT >= 0o100650