Coding Gun

Sonarqube คืออะไร? และใช้งานยังไง?

SonarQube ถือได้ว่าเป็นเครื่องมือที่หลายๆคนเลือกมาเป็นส่วนหนึ่งใน CI/CD pipeline ถือได้ว่าเป็นเครื่องมือที่ช่วยให้เราจัดการกับปัญหาที่รับมือได้ยากมากๆ นั่นคือ Code Quality

SonarQube คืออะไร?

SonarQube คือ Quality Management Tool หรือเครื่องมือที่ใช้ในการจัดการกับ Code Quality ซึ่งในช่วงหลัง SonarQube จะค่อนข้างโด่งดังในเรื่อง Security ส่วนใหญ่เราจะใช้ SonarQube Scan เพื่อหาช่องโหว่ใน source code แต่จริงๆแล้ว Sonarqube จะแยกปัญหาออกมาเป็น 4 กลุ่ม ดังนี้

  1. Bug คือ rules ที่จะตรวจสอบ source code ที่น่าจะเขียนผิด เช่น

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    int? nullable = 42;
    // Noncompliant, จะเป็น false เสมอ
    bool comparison = nullable.GetType() == typeof(Nullable<int>);
    // Noncompliant, จะเป็น true เสมอ
    comparison = nullable.GetType() != typeof(Nullable<int>); 
    
    nullable = null;
    // Noncompliant, การเรียก method .GetType ของตัวแปรที่เป็น null จะทำให้เกิด exception
    comparison = nullable.GetType() != typeof(Nullable<int>); 
    

    จาก rule ด้านบนเป็นภาษา c# การเปรียบเทียบประเภทของข้อมูลโดยใช้ method .GetType() function typeof ซึ่งค่าที่ได้จะไม่มีทางเท่ากัน เนื่องจาก .GetType() จะ return int เพราะเรากำหนดค่า 42 ให้กับตัวแปร nullable แล้ว แต่ในขณะที่เราใช้ typeof จะ return Nullable กลับมา ดังนั้นมันจะไม่มีทางเท่ากัน

    สรุปง่ายๆคือ เราถาม type ตอนประกาศตัวแปรเราจะใช้ typeof ซึ่งได้คำตอบเป็น nullable type แต่ .GetType() เป็นการถามตอนที่สร้าง object ขึ้นมาแล้วดังนั้่นจะมี 2 ทางคือ

    • ถ้าใช้ .GetType() ตอนเป็น null ก็จะเกิด exception ขึ้นเพราะ null ไม่มี method นี้
    • ถ้าใช้ .GetType() ตอนเป็น int ก็จะได้คำตอบออกมาเป็น int

    ดังนั้นไม่ว่าจะเป็นทางไหน ก็ไม่ได้ตอบเป็น nullable type ดังนั้น ในบรรทัดที่ 3 จึงเป็น false เสมอ

  2. Code smell คือ rules ที่จะตรวจสอบ code ที่อ่านยากหรือแก้ไขได้ยาก เช่น

    1
    2
    3
    4
    5
    6
    7
    8
    
    public class Program
    {
        // Non-Compliant, array 2 มิติ ไม่ได้บอกว่าต้องส่งอะไรเข้ามาบ้าง
        public void WriteMatrix(int[][] matrix)
        {
            ...
        }
    }
    

    ใน c# จะไม่ให้ใช้ multidimention array เป็น parameter เพราะอ่านยาก ควรสร้างเป็น object หรือ array ของ object แล้วโยนเข้าไปจะทำให้อ่าน code แล้วเข้าใจได้ง่ายกว่า

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    public class Matrix
    {
        // ในนี้จะมี properties ที่บอกเราได้ว่าจะต้องส่งอะไรไปบ้าง
    }
    
    public class Program
    {
        public void WriteMatrix(Matrix matrix) // Compliant
        {
            ...
        }
    }
    
  3. Vulnerability คือ rules ที่จะตรวจสอบ code ที่เขียนแล้วน่าจะมีช่องโหว่ เช่น

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    public class ExampleController : Controller
    {
        private readonly UserAccountContext Context;
    
        public IActionResult Authenticate(string user, string pass)
        {
            // Non-compliant, เอาตัวแปร user ไปต่อใน string ที่นำไป query จะทำให้เกิด sql injection
            var query = "SELECT * FROM users WHERE user = '" + user + "' AND pass = '" + pass + "'";
    
            ...
        }
    }
    

    เอาตัวแปรไปต่อกับ sql command โดยใช้เครื่องหมาย “+” ในการเชื่อม string จะทำให้เกิดช่องโหว่ที่ชื่อ SQL Injection

  4. Security hotspot คือ rules ที่อาจเกิดเป็นช่องโหว่ได้ คล้่ายกับ vulnerability แต่ sonarqube ยังไม่แน่ใจจึงต้องการให้เราเข้าไป review เช่น

    1
    2
    3
    4
    5
    6
    
    // Sensitive use of Random
    var random = new Random(); 
    byte[] data = new byte[16];
    random.NextBytes(data);
    // Check if this value is used for hashing or encryption
    return BitConverter.ToString(data); 
    

    ในตัวอย่างด้านบนเป็นปัญหาที่เกิดจากการใช้ function random ที่ไม่่ secure ซึ่งการใช้ random สามารถใช้ได้กับ business case ทั่วไป เช่น random การแสดงผลของ blog post แต่จะไม่สามาถใช้กับการ hashing หรือ excryption ที่ต้องการปกปิดความลับได้ เพราะฉะนั้นเราจึงต้อง review ว่าค่าที่ return ออกไปนี้เอาไปใช้ทำอะไร

    ถ้าส่วนไหนที่ต้อง random แล้วนำไปใช้กับการ hashing และ encryption เราต้องใช้ Cryptographic function แบบนี้ ภาษาอื่นๆสามารถดูรายละเอียดเพิ่มเติมได้จาก recommended ของ OWASP

    1
    2
    3
    4
    5
    6
    
    using System.Security.Cryptography;
    ...
    var randomGenerator = RandomNumberGenerator.Create(); // Compliant for security-sensitive use cases
    byte[] data = new byte[16];
    randomGenerator.GetBytes(data);
    return BitConverter.ToString(data);
    

SonarQube ประกอบไปด้วย

การจะใช้งาน SonarQube จำเป็นต่้องมีสิ่งที่ต้องติดตั้ง 3 ส่วนหลักๆ ดังนี้

  1. Sonar Scanner คือเครื่องมือที่ใช้ในการ scan source code ดังนั้นต้องติดตั้งในเครื่องที่มี source code เช่นถ้า scan บนเครื่อง dev ก็ต้องติดตั้ง sonar scanner บนเครื่อง dev แต่ถ้า scan บนเครื่องที่ทำ CI/CD เราก็ต้องไปติดตั้ง sonar scanner ใน server ที่ run pipeline นี้
  2. SonarQube Server คือ server ที่จัดเก็บผลลัพธ์ที่ได้จากการ scan เป็นแกนหลักของการ scan source code ด้วย sonarqube
  3. Database Server เนื่องจาก sonarqube server นั้นทำการเก็บข้อมูลอยู่ใน memory เท่านั้น ดังนั้นเราเลยต้องทำการ link database เข้าไปเพื่ิอจะได้เก็บผลลัพธ์ที่ได้จากการ scan ให้คงอยู่ถาวร ซึ่ง database ที่ sonarqube support นั้นประกอบด้วย
    • PostgreSql
    • Microsoft SQL Server
    • Orable

Default admin credentials

หลังจาก install sonarqube server เสร็จเรียบร้อย ระบบจะทำการสร้าง user ที่มีสิทธิเป็น Admin ให้โดยอัตโนมัติ เราสามารถ Login เข้าไปในระบบด้วย user นี้

Login: admin
Password: admin

วิธีใช้งาน SonarQube เบื้องต้น

การใช้งาน SonarQube จะมีอยู่ 3 ทางหลักๆ คือ

  1. run sonar-scanner ผ่านทาง command-line, maven หรือ gradle(manual)
  2. ติดตั้ง sonar lint ใน editor ที่เราใช้งานอยู่แล้ว config ให้ส่งผลลัพธ์ขึ้นไปบน server(auto)
  3. run sonar-scanner ผ่านทาง CI/CD pipeline(auto)

ซึ่งในตัวอย่างนี้จะเป็นการ scan ผ่านทาง command-line บน windows

Generate token บน SonarQube server

  1. login เข้าไปใน sonarqube server
  2. เลือก menu project
    Show list of Sonarqube Projects
  3. กดปุ่ม create project
    Create Sonarqube Project
  4. เลือกการติดตั้งแบบ Manually
  5. ตั้ง project key(ใน server เดียวกันจะไม่สามารถใช้ key ซ้ำกันได้)
    Setup sonarqube porject
    project key จะต้องเป็น unique แต่ display name จะใช้ในการแสดงผลสามารถซ้ำกันได้
  6. ตั้งชื่อของ Token แล้วกดปุ่ม Generate Key แล้วก็ copy key นี้เอาไว้ใช้ในขั้นตอนถัดไป
    Generate Token
    Copy token นี้ไปไว้ในที่ๆปลอดภัยเราจะไม่สามารถเรียก token กลับมาดูได้อีกแล้ว

Generate Token ใหม่โดยที่ไม่ได้ create project

เราสามารถเข้าไปจัดการกับ token ได้โดยที่ไม่ต้อง create project ซึ่งเราต้องเข้าไปที่

  1. เปิด My Account

    Sonarqube user profile

  2. เลือก tab Security

    Sonarqube token management

  3. ให้ตั้งชื่อ token ที่จำได้ง่ายว่าเอาไปใช้ทำอะไร

  4. เลือก type เป็น Project analysis tokens

  5. กำหนด Expires in ตามต้องการ(ยิ่งกำหนดให้นานเท่าไหร่ยิ่งไม่ปลอดภัย)

  6. กดปุ่ม Generate แล้วก็ copy token ออกมาใช้งานได้เลย

Token ไหนไม่ใช้แล้วหรือมีความเสี่ยงที่จะโดนขโมยให้เราทำการ Revoke ออกทันที

วิธีการ scan ด้วย sonar-scanner

  1. ติดตั้ง sonar-scanner โดย download sonar-scanner-cli ได้ตาม link นี้

  2. เลือก windows-64 bits

    Download sonarqube scanner
    เลือก download sonar scanner ตาม platform ของเรา

  3. สร้าง ไฟล์ sonar-project.properties(ไฟล์ที่กำหนด configuration) ไว้ที่เดียวกับ source code โดยใส่เนื้อหาดังนี้

    # ต้องกำหนดตอนสร้าง project บน sonarqube server
    # project key ห้ามซ้ำกัน
    sonar.projectKey=my:project
    
    # --- optional properties ---
    
    # ชื่อ project ถ้าไม่กำหนด sonar-scanner จะใช้ project key แทนก
    sonar.projectName=My project
    
    # กำหนด version ของ source code ชุดนี้
    # ควรจะ update version ไปเรื่อยๆ
    sonar.projectVersion=1.0
    
    # ระบุว่า source code อยู่ใน folder ไหน
    # default จะเป็น . ซึ่งคือ directory ที่ไฟล์ sonar-project.properties อยู่
    sonar.sources=.
    
    # ระบุว่า sonarqube server อยู่ที่ไหน
    sonar.host.url=http://localhost:9000
    
    # เราต้องได้ Token จาก server ถึงจะสามาถส่งผลลัพธ์ขึ้นไปยัง Sonarqube Server ได้
    # เราจะได้ token ตอน create project หรือเข้าไปใน user profile เพื่อ generate token ใหม่
    sonar.login= [Token ที่ได้จาก Sonarqube Server]
    
    # รอผลลัพธ์จาก Sonarqube Server ว่าผ่านหรือไม่ผ่านเงื่อนไขที่เรากำหนดไว้ใน quality gate
    sonar.qualitygate.wait=true
    
  4. สั่ง run command นี้ใน directory ที่มีไฟล์ sonar-project.properties

    sonar-scanner
    
  5. กลับไปดูผลลัพธ์บน SonarQube Server

คำศัพท์ที่ต้องรู้จักก่อนใช้ SonarQube

ก่อนจะใช้งาน sonarqube เราลองมาทำความเข้าใจศัพท์แต่ละตัวที่ต้องใช้ใน sonarqube กันก่อน

Rules

rules คือกฎต่างๆที่ sonarqube เขียนขึ้นมาและถูกเรียกใช้โดย default ซึ่ง rules ของ sonarqube นั้นจะชื่อว่า sonar-way แต่นอกจาก sonar-way แล้วเรายังสามารถ download ของค่ายอื่นๆ ผ่านทาง marketplace เพิ่มเติมได้อีกด้วย

Quality Profiles

Quality profiles จะเป็น set ของ rules เราสามารถ download Quality profile เพิ่มเติมได้จากหน้า marketplace หรืออาจ custom quality profiles ของเราได้โดยทำการ copy quality profile ออกมาแล้วก็เพิ่มหรือลด rule ได้ตามต้องการ

Quality Gate

เป็นเงื่อนไขที่เราสามารถกำหนดได้ว่า Quality ที่เราอยากได้นั้นอยู่ในระดับไหน เช่น จำนวน vulnerabilities ต้องไม่เกินเท่าไหร่ ซึ่งสิ่งที่เราต้องกำหนดมี 3 อย่างคือ

Duplicate Code

Code ที่เขียนแล้วซ้ำกัน ซึ่ง sonarqube จะทำการค้นหา code ส่วนที่ซ้ำกันแล้วก็สรุปผลไว้ในหน้า dashboard ว่า code เรา duplicated กี่เปอร์เซ็น ดังรูป

Duplicated Code Summary on dashboard
ใน dashboard จะมีสรุปผลว่า code เราซ้ำกันกี่เปอร์เซ็น

เมื่อเราคลิกที่รูปแว่นขยายก็จะเข้าสู่หน้า report ดังรูป

Duplicated Code Report
วงกลมยิ่งมีขนาดใหญ่แสดงว่ายิ่งซ้ำเยอะ

สิ่งที่เราต้องสนใจจะอยู่ที่มุมซ้ายบน ซึ่งจะเป็นส่วนที่มี duplicated lines(จำนวนบรรทัดที่ซ้ำกัน) เยอะมากกว่าส่วนอื่น เมื่อเราคลิกเข้าไป sonarqube จะพาไปยัง source code ส่วนที่มีการซ้ำกัน และเรายังสามารถกดเข้าไปดูว่ามีไฟล์ไหนที่ใช้ code นี้บ้าง(ต้องกด ที่ bar สีเทาหนึ่งครั้งให้แสดงแถบ bar สีเทาหลายๆแถบก่อน) ดังรูป

Show Duplicated lines
คลิกที่ bar สีเทาหน้า source code จะแสดงรายชื่อไฟล์ที่ใช้ code ส่วนนี้เหมือนกัน

อ่านต่อเพิ่มเติมได้ที่

Phanupong Permpimol
Follow me

Software Engineer ที่เชื่อในเรื่องของ Process เพราะเมื่อ Process ดี Product ก็จะดีตาม ปัจจุบันเป็นอาจารย์และที่ปรึกษาด้านการออกแบบและพัฒนา Software และ Web Security