Hook

Hook regex: ตรวจหา pattern ที่เสี่ยงต่อ ReDoS

สแกนโค้ดเบสหา regex ที่เสี่ยงต่อ ReDoS หรือประเมิน pattern เดียว ได้จากบรรทัดคำสั่ง

hook regex คือเอนจินตรวจความปลอดภัยของ regex แบบ deterministic ที่ ความรู้เฉพาะทาง regex ใช้ และคุณรันเองได้ hook นี้ทำงานได้สองโหมดจากคำสั่งเดียว: โหมด scan ที่สแกนโค้ดเบสหา pattern ที่เสี่ยงต่อ ReDoS และ pattern ที่สร้างขึ้นตอน runtime และโหมด check ที่ประเมิน pattern เดียวที่คุณส่งให้

สแกนโค้ดเบส​

หากไม่ใส่ flag hook จะสแกนทั้งโปรเจกต์ --dir จำกัดการสแกนไว้ที่ไดเรกทอรีเดียว และ --file จำกัดไว้ที่ไฟล์เดียว ทั้งสองตัวเลือกใส่ซ้ำได้และรวมผลเป็นรายงานเดียว โดยแต่ละไฟล์จะแสดงเพียงครั้งเดียวแม้มีเป้าหมายมากกว่าหนึ่งรายการครอบคลุมไฟล์นั้น

npx lagune read regex

ผลลัพธ์เป็นข้อความดิบที่หัวข้อของแต่ละส่วนบอกวิธีอ่านส่วนนั้น และจะแสดงส่วนใดก็ต่อเมื่อส่วนนั้นมีข้อตรวจพบเท่านั้น

Vulnerable regular expressions found:

cmd/server/main.go
([a-z]+)+$ (exponential, search)

Dynamically built regular expressions (review manually by simulating a constructed regex):

app/payments.php
crates/parser/lexer.rs
lib/validate.py

Static regex wrapped in a constructor (use a literal instead):

config/token.rb

เมื่อไม่พบอะไร hook จะพิมพ์ออกมาเพียงบรรทัดเดียว

npx lagune read regex --dir src/clean
# => no unsafe patterns found

วิธีอ่านแต่ละส่วน​

ส่วนความหมาย
Vulnerable regular expressions foundpattern ที่เสี่ยงต่อ ReDoS จัดกลุ่มตามไฟล์ โดยแต่ละ pattern เยื้องอยู่ด้านล่าง ตามด้วยเหตุผลที่ไม่ปลอดภัยในวงเล็บ และขอบเขตกับ flag ของ pattern นั้นหากมี ทุกบรรทัดไม่ปลอดภัย จึงไม่แสดงผลตัดสินซ้ำ
Regular expressions the analysis could not settle (review manually)pattern ที่การวิเคราะห์สรุปไม่ได้ภายในขีดจำกัดการทำงาน แสดงในรูปแบบเดียวกัน
Dynamically built regular expressions (review manually by simulating a constructed regex)ไฟล์ที่สร้าง regex จากตัวแปร การต่อสตริง หรือการแทรกค่า (interpolation)
Static regex wrapped in a constructor (use a literal instead)ไฟล์ที่ส่งสตริง literal ธรรมดาให้ constructor ของ regex ในกรณีที่ regex literal เรียบง่ายกว่าและไม่ต้องคอมไพล์ตอน runtime

ส่วนแรกคือข้อตรวจพบด้านความปลอดภัย ส่วนที่สองและสามไม่ใช่ข้อตรวจพบในตัวเอง: ทั้งสองส่วนชี้ไปยังโค้ดที่การตรวจแบบ static สรุปไม่ได้ จึงต้องให้คนอ่าน ส่วนที่สี่เป็นคำแนะนำเพื่อให้โค้ดชัดเจนขึ้น ไม่ใช่ข้อตรวจพบด้านความปลอดภัย

ตัวเลือก CLI​

ตัวเลือกค่าคำอธิบาย
--patternซอร์สของ regexตรวจ pattern เดียว ใส่ซ้ำเพื่อตรวจหลาย pattern โดยได้ผลตัดสิน pattern ละหนึ่งรายการตามลำดับ
--flagsflag ของ regexตรวจทุก --pattern ด้วย flag เหล่านี้ เช่น im ใช้กับโหมด check เท่านั้น
--dirไดเรกทอรีจำกัดการสแกนไว้ที่ไดเรกทอรีเดียว ใช้ซ้ำได้และใช้ร่วมกับ --file ได้
--fileไฟล์จำกัดการสแกนไว้ที่ไฟล์เดียว ใช้ซ้ำได้และใช้ร่วมกับ --dir ได้
--limitจำนวนเต็มที่ไม่ติดลบกำหนดขีดจำกัดการทำซ้ำของ ReDoS (ค่าเริ่มต้น 25) ใช้กับทั้งสองโหมด ขีดจำกัดที่ต่ำกว่าจะเข้มงวดกว่า

หากไม่ระบุตัวเลือก hook จะสแกนทั้งโปรเจกต์ --pattern คือโหมด check และใช้ร่วมกับ --dir หรือ --file ไม่ได้

เลือกสิ่งที่จะสแกน​

hook นี้ข้ามสิ่งที่ Git ละเว้นไว้ หากต้องการข้ามมากกว่านั้น หรือต้องการสแกนสิ่งที่ Git ละเว้นไว้ ให้กำหนดการตั้งค่าเฉพาะของ regex เอง:

npx lagune write add --ignore test/fixtures --checker regex
{
"checkers": {
"regex": { "gitignore": false, "ignore": ["test/fixtures"] }
}
}

กำหนดค่า hook อธิบายการตั้งค่าทั้งหมด

ภาษาที่รองรับ​

การสแกนอ่านภาษาเหล่านี้โดยแยกตามนามสกุลไฟล์ และอ่านแต่ละภาษาตามกฎของภาษานั้นเอง hook จึงไม่มีวันทดสอบ pattern จากภาษาหนึ่งด้วยกฎของอีกภาษา และข้ามไฟล์อื่นทั้งหมดแทนที่จะสแกนด้วยกฎที่ผิด

#ภาษานามสกุลไฟล์
1JavaScript / TypeScript.js, .jsx, .mjs, .cjs, .ts, .tsx, .mts, .cts, .astro, .vue, .svelte, .marko, .riot
2Python.py, .pyi
3Ruby.rb
4Go.go
5PHP.php
6Rust.rs
7Java.java
8C#.cs
9C.c, .h
10C++.cc, .cpp, .cxx, .hpp, .hh, .hxx
11Kotlin.kt, .kts
12Swift.swift
13Scala.scala, .sc
14Dart.dart
15PowerShell.ps1, .psm1, .psd1
16Elixir.ex, .exs
17Objective-C.m, .mm
18R.r
19Julia.jl
20Clojure.clj, .cljs, .cljc, .edn
21Crystal.cr
22Nim.nim, .nims
23V.v
24D.d
25Perl.pl, .pm
ตรวจให้ได้มากที่สุด แต่ไม่ครบทุกกรณี

hook นี้อ่านซอร์สโค้ดเป็นข้อความ จึงตัดสิน pattern ที่ประกอบขึ้นตอน runtime ไม่ได้ และ pattern ที่เขียนแยกหลายบรรทัด ซ่อนอยู่หลัง alias หรือเขียนในรูปแบบที่ hook ไม่รู้จัก อาจหลุดรอดไปได้ ให้ถือว่าส่วนต่าง ๆ เหล่านี้เป็นจุดเริ่มต้นที่หนักแน่น ไม่ใช่รายการที่ครบถ้วน และอ่านโค้ดต่อในจุดที่ส่วนเหล่านี้เข้าไม่ถึง

ตรวจ pattern เดียว​

ส่ง --pattern พร้อมซอร์สของ pattern เพื่อประเมิน hook จะพิมพ์คำเดียว: safe, unsafe, undetermined เมื่อการวิเคราะห์ถึงขีดจำกัดการทำงานก่อนสรุป pattern ได้ หรือ invalid regex ใส่ --pattern ซ้ำเพื่อประเมินหลาย pattern พร้อมกัน โดยได้ผลตัดสิน pattern ละหนึ่งรายการตามลำดับ

npx lagune read regex --pattern '(a+)+$'
# => unsafe

npx lagune read regex --pattern '^[a-z0-9_]{3,20}$'
# => safe
ทำไมต้องครอบอาร์กิวเมนต์ด้วยเครื่องหมายคำพูด

pattern แต่ละรายการส่งเข้าไปเป็นค่าของ flag และไม่เคยนำไปแทรกลงในคำสั่งโดยตรง ค่าที่มีเครื่องหมายคำพูดหรือ backtick จึงไม่มีผลใด ๆ และ inject เข้า shell ไม่ได้ ครอบ pattern ด้วยเครื่องหมายคำพูดเดี่ยวเสมอ เพื่อไม่ให้ shell ของคุณขยายค่านั้นก่อน

tip

นี่คือเอนจินเดียวกับที่ ความรู้เฉพาะทาง regex ใช้: การสแกนเปิดรอบการตรวจ ReDoS และการตรวจแบบ check ยืนยัน pattern ที่การสแกนพบ

คำถามที่พบบ่อย

รัน npx lagune read regex --pattern '<pattern>' แล้ว hook จะคืนค่าเป็น safe, unsafe, undetermined หรือ invalid regex

รัน hook regex ในโหมด scan กับโค้ดเบส แล้ว hook จะรายงาน pattern ที่เสี่ยงต่อ ReDoS ในภาษาที่รองรับทั้ง 25 ภาษา