2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Semgrep・OWASP ZAP・Trivyで始めるSAST/DAST/SCA自動化実践ガイド

2
Last updated at Posted at 2026-03-18

Semgrep・OWASP ZAP・Trivyで始めるSAST/DAST/SCA自動化実践ガイド

この記事でわかること

  • SAST・DAST・SCAの3つのセキュリティテスト手法の違いと適用タイミング
  • Semgrep + CodeQL + OWASP ZAP + Trivyを組み合わせたCI/CDパイプラインの構築方法
  • GitHub Actionsで実装するDevSecOpsワークフローの具体的なYAML設定
  • 偽陽性を70-90%削減する到達可能性分析とアラートチューニングの実践手法
  • 導入から成熟まで12週間のロードマップと成功指標の設計

対象読者

  • 想定読者: CI/CDパイプラインの基本を理解しているバックエンド・インフラエンジニア
  • 必要な前提知識:
    • GitHub Actionsの基本的なワークフロー記法(YAML)
    • Docker の基本操作(docker run, docker pull
    • OWASP Top 10の概要レベルの理解

セキュリティテストの専門知識は不要です。「SAST」「DAST」「SCA」という用語を初めて聞く方でも、この記事で基礎から実装まで理解できる構成にしています。

結論・成果

SAST・DAST・SCAの3層セキュリティテストをCI/CDに統合することで、以下の効果が報告されています。

  • 脆弱性の早期検出: PRレベルでのSASTスキャンにより、修正コストがリリース後の修正と比較して大幅に低減(IDEでの修正は数分、本番環境での修正は数百万円規模のコストになりうるとCORE Systems社の報告で指摘)
  • 偽陽性の削減: 到達可能性分析(Reachability Analysis)の導入により、アラート数を70-90%削減(AppSec Santaの報告)
  • スキャン速度: Semgrepの中央値スキャン時間は10秒、パイプラインへの影響は60秒未満(AppSec Santa Semgrep vs CodeQL比較
  • OSSの脆弱性カバレッジ: Sonatype 2025年調査によると、ダウンロードされるOSSコンポーネントの96%に既知の脆弱性が含まれている(CORE Systems

SAST・DAST・SCAの基礎を理解する

セキュリティテスト自動化を始める前に、3つの手法の違いと適用タイミングを整理しましょう。これはML(機械学習)に例えると、SASTはモデルのコードレビュー(静的チェック)、DASTは推論エンドポイントへの負荷テスト(動的チェック)、SCAは依存ライブラリの脆弱性スキャン(pip auditに相当)と考えるとイメージしやすいでしょう。

3手法の比較

項目 SAST DAST SCA
正式名称 Static Application Security Testing Dynamic Application Security Testing Software Composition Analysis
検査対象 ソースコード・バイトコード 実行中のアプリケーション OSS依存パッケージ
検査タイミング コーディング~ビルド時 デプロイ後(ステージング) ビルド時
検出できる脆弱性 SQLインジェクション、XSS、ハードコードされた秘密情報 認証バイパス、設定ミス、CSRF 既知のCVE、ライセンス違反
実行時間の目安 10秒~30分(ツールによる) 5分~60分以上 60秒未満
偽陽性率 中~高
代表的なOSSツール Semgrep、CodeQL OWASP ZAP、Nuclei Trivy、Grype

なぜ3つとも必要なのか

「SASTだけ入れればいいのでは?」と考えるかもしれません。しかし、各手法には検出の死角があります。

  • SASTでは見つからない: 実行時の設定ミス(HTTPSの無効化、CORSの過剰許可)やビジネスロジックの脆弱性
  • DASTでは見つからない: ソースコード内のハードコードされたAPI鍵、未使用だが脆弱なコードパス
  • SCAでは見つからない: 自分で書いたコードのバグ、サードパーティライブラリの使い方の間違い

Checkmarx社の解説では、SASTでコード品質、SCAでOSS依存関係、DASTで実行時の挙動をカバーする3層アプローチを推奨しています。

注意: 3つのツールを同時に導入しようとすると、アラート過多で開発チームが疲弊します。後述する段階的な導入ロードマップに沿って、まずSAST+SCAから始め、DASTは安定してから追加するのが実践的です。

SASTをCI/CDに統合する:Semgrep + CodeQLの併用パターン

SAST(静的アプリケーションセキュリティテスト)は、ソースコードを解析して脆弱性パターンを検出します。2026年現在、PRレベルの高速スキャンにはSemgrep、深い解析にはCodeQLを使う併用パターンが広がっています。InfoQの報告によると、LinkedInはこの方式でGitHub Actions上に数千リポジトリのSASTパイプラインを構築しました。

Semgrep vs CodeQL:選定基準

項目 Semgrep CodeQL
スキャン速度 中央値10秒 数分~30分超
メモリ使用量 約150MB 約450MB
対応言語数 30以上(Terraform、Dockerfile含む) 12言語(C/C++、Go、Java、Python、JS/TS等)
カスタムルール記法 YAML(数分で作成可能) QL言語(習得に数日)
精度 CE版は中程度、Code版は高い 高い(偽陽性率が低い)
料金 CE無料 / Teams $35/月/contributor 公開リポジトリ無料 / プライベートはGHAS必要

出典: AppSec Santa - Semgrep vs CodeQL

GitHub ActionsでSemgrepを設定する

まずはPRごとに高速スキャンを実行するSemgrepのワークフローを作成します。

# .github/workflows/semgrep.yml
name: Semgrep SAST Scan
on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep:latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
        run: |
          semgrep ci \
            --sarif \
            --output=semgrep-results.sarif \
            --severity=ERROR \
            --severity=WARNING

      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep-results.sarif

ポイント:

  • --severity=ERROR --severity=WARNING で情報レベルのノイズを除外します
  • SARIF(Static Analysis Results Interchange Format)形式で出力し、GitHubのCode Scanningダッシュボードに統合します
  • SEMGREP_APP_TOKEN はSemgrep Platformと連携する場合に設定します。ローカルルールのみなら不要です

CodeQLの週次ディープスキャンを追加する

Semgrepでは検出が難しいデータフロー解析(汚染追跡)をCodeQLで補完します。CodeQLはスキャン時間が長いため、スケジュール実行にしてPRをブロックしないようにします。

# .github/workflows/codeql.yml
name: CodeQL Deep Analysis
on:
  schedule:
    - cron: '0 3 * * 1'  # 毎週月曜 AM3:00 UTC
  workflow_dispatch:

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    strategy:
      matrix:
        language: [python, javascript-typescript]
    steps:
      - uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          queries: security-extended

      - name: Autobuild
        uses: github/codeql-action/autobuild@v3

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
        with:
          category: "/language:${{ matrix.language }}"

なぜこの構成を選んだか:

  • Semgrep(PR時)で10秒以内のフィードバックを提供し、開発者体験を損なわない
  • CodeQL(週次)で深いデータフロー解析を補完し、Semgrepの検出漏れをカバー
  • 両ツールともSARIF出力に対応し、GitHub Code Scanningに結果を集約できる

注意: CodeQLのsecurity-extendedクエリセットはdefaultより多くのルールを含みますが、偽陽性も増加します。最初はdefaultで始め、チームが運用に慣れてからsecurity-extendedに切り替えるのを推奨します。

カスタムルールで組織固有の脆弱性を検出する

Semgrepの大きな強みは、YAML形式で独自ルールを簡単に作成できる点です。たとえば、SQLインジェクションの可能性があるf-string内のSQL文を検出するルールは次のように書けます。

# .semgrep/rules/sql-fstring.yml
rules:
  - id: python-sql-fstring-injection
    patterns:
      - pattern: |
          $CURSOR.execute(f"...{$VAR}...")
      - pattern-not: |
          $CURSOR.execute(f"...{$CONST}...", ...)
    message: |
      f-string内で直接変数を展開したSQL文が検出されました。
      パラメータ化クエリを使用してください。
    severity: ERROR
    languages: [python]
    metadata:
      category: security
      cwe: ["CWE-89"]
      owasp: ["A03:2021"]

MLエンジニアの方には馴染み深い例として、モデル推論APIでユーザー入力をそのままSQLに埋め込むパターンをこのルールで検出できます。Pythonのcursor.execute(f"SELECT * FROM models WHERE name='{user_input}'")のようなコードは、パラメータ化クエリcursor.execute("SELECT * FROM models WHERE name=%s", (user_input,))に置き換える必要があります。

SCAで依存パッケージの脆弱性を管理する:Trivy + Dependabot

SCA(Software Composition Analysis)は、プロジェクトが依存するOSSライブラリの既知脆弱性を検出します。Pythonのpip auditやNode.jsのnpm auditを使ったことがある方には馴染みのある概念です。SCAツールはそれらをより広範囲に、かつCI/CDに統合した形で実行します。

ツール選定:Trivy vs Snyk vs Dependabot

項目 Trivy Snyk Dependabot
ライセンス OSS(MIT) 商用(無料枠あり) GitHub無料機能
スキャン対象 コンテナ、ファイルシステム、IaC SCA、SAST、コンテナ、IaC 依存パッケージのみ
到達可能性分析 なし あり(偽陽性80%削減) なし
自動修正PR なし あり あり
CI/CD統合 CLI(どこでも動作) SaaS(コード送信が必要) GitHub専用
コスト 無料 $25-110/開発者/月 無料

出典: Aikido Security - Snyk vs Trivy

このガイドでの選択: Trivy(CIでの脆弱性スキャン)+ Dependabot(自動更新PR生成)の組み合わせを採用します。理由は以下の通りです。

  • Trivyは単一バイナリでコンテナ・ファイルシステム・IaCを横断スキャンでき、アカウント登録やクラウドへのコード送信が不要
  • DependabotはGitHub組み込み機能として設定なしで脆弱性アラートを受け取れ、自動更新PRも生成
  • 商用ツールの導入は、この基盤が安定してから検討するのが費用対効果の面で実践的

TrivyをGitHub Actionsに統合する

# .github/workflows/trivy-sca.yml
name: Trivy SCA Scan
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  trivy-fs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy filesystem scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: fs
          scan-ref: .
          severity: HIGH,CRITICAL
          exit-code: 1
          format: sarif
          output: trivy-fs-results.sarif

      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-fs-results.sarif

  trivy-image:
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -t app:${{ github.sha }} .

      - name: Scan Docker image
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: app:${{ github.sha }}
          severity: HIGH,CRITICAL
          exit-code: 1
          format: sarif
          output: trivy-image-results.sarif

      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-image-results.sarif

Dependabotの設定

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: pip
    directory: /
    schedule:
      interval: weekly
      day: monday
    open-pull-requests-limit: 10
    labels:
      - dependencies
      - security
    groups:
      production-deps:
        patterns:
          - "*"
        exclude-patterns:
          - "pytest*"
          - "ruff"

  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    open-pull-requests-limit: 5

  - package-ecosystem: docker
    directory: /
    schedule:
      interval: weekly

  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly

ポイント:

  • groupsを使って関連する依存パッケージをまとめ、PRの数を抑制します。個別PRが大量に来る「Dependabot疲れ」を防ぐ設計です
  • PRが管理不能になった場合は、Renovateへの移行を検討します。Renovateはグルーピング、スケジューリング、自動マージルールがより柔軟です(AppSec Santa

ハマりポイント: Trivyのseverity: HIGH,CRITICAL設定でCIを止めると、初回実行時に大量の既存脆弱性でパイプラインがブロックされます。まずexit-code: 0(警告モード)で2-4週間運用し、既存の脆弱性を棚卸ししてからexit-code: 1に切り替えるのが実践的です。DevSecOps Implementation Guideでも同様のアプローチが推奨されています。

DASTでデプロイ済みアプリの脆弱性を検出する:OWASP ZAP

DAST(動的アプリケーションセキュリティテスト)は、実行中のアプリケーションに対して擬似的な攻撃リクエストを送信し、脆弱性を検出します。MLエンジニアの方には、推論エンドポイントに対するAdversarial Testingの概念に近いと考えてください。

OWASP ZAPの2つのスキャンモード

OWASP ZAP(Zed Attack Proxy)はDASTツールのデファクトスタンダードです。CI/CDではBaseline ScanFull Scanの2つのモードを使い分けます。

スキャンモード 実行時間 検出範囲 適用タイミング
Baseline Scan 1-5分 パッシブスキャンのみ(攻撃なし) PR・デプロイ後の毎回実行
Full Scan 30分-数時間 アクティブスキャン(擬似攻撃含む) 週次スケジュール実行

Baseline ScanをGitHub Actionsに統合する

ステージング環境へのデプロイ後に自動実行するワークフローです。

# .github/workflows/zap-dast.yml
name: OWASP ZAP DAST Scan
on:
  workflow_run:
    workflows: ["Deploy to Staging"]
    types: [completed]

jobs:
  zap-baseline:
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.14.0
        with:
          target: ${{ vars.STAGING_URL }}
          rules_file_name: .zap/rules.tsv
          cmd_options: >-
            -a
            -j
            -z "-config api.disablekey=true"
          allow_issue_writing: false

      - name: Upload ZAP Report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: zap-baseline-report
          path: report_html.html

ルールのチューニング

ZAPのデフォルト設定では偽陽性が多く出ます。.zap/rules.tsvファイルでルールを調整しましょう。

# .zap/rules.tsv
# OUTOFSCOPE: 検査対象外、IGNORE: 結果無視、WARN: 警告のみ、FAIL: ビルド失敗
10003	WARN	# Vulnerable JS Library (SCAで別途カバー)
10015	IGNORE	# Incomplete or No Cache-control Header (機能に影響なし)
10021	WARN	# X-Content-Type-Options Header Missing
10038	IGNORE	# Content Security Policy Header Not Set (SPA対応で個別検討)
90033	WARN	# Loosely Scoped Cookie

なぜこのチューニングか:

  • SCAで検出済みの脆弱性(JS Libraryの脆弱性)をDASTでも検出すると重複アラートになるためWARNに抑制
  • キャッシュヘッダーやCSPはセキュリティポリシー全体で対応するもので、個別のDASTアラートとして扱うと判断が難しい

Full Scanの週次スケジュール実行

# .github/workflows/zap-full.yml
name: ZAP Full Scan (Weekly)
on:
  schedule:
    - cron: '0 2 * * 0'  # 毎週日曜 AM2:00 UTC
  workflow_dispatch:

jobs:
  zap-full:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: ZAP Full Scan
        uses: zaproxy/action-full-scan@v0.12.0
        with:
          target: ${{ vars.STAGING_URL }}
          rules_file_name: .zap/rules.tsv
          cmd_options: "-a -j"

      - name: Upload Full Scan Report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: zap-full-report
          path: report_html.html

制約条件: OWASP ZAPのFull Scanはステージングサーバーに対して実際の攻撃リクエストを送信します。本番環境に対して実行すると、サービス停止や意図しないデータ変更のリスクがあるため、必ずステージング環境のみを対象にしてください。また、共有ステージングの場合は他チームとスキャンスケジュールを調整する必要があります。

偽陽性と向き合う:アラートチューニングの実践

セキュリティテスト自動化で最も多い失敗は、ツールの導入ではなくアラート管理です。DevSecOps Implementation Guideによると、偽陽性率が30%を超えるとツールの信頼性が損なわれ、開発者がアラートを無視し始めるという調査結果があります。

偽陽性削減の3つのアプローチ

1. 段階的な閾値引き上げ

最初から全てのSeverityでビルドを止めてはいけません。

# Phase 1(導入初月): CRITICALのみブロック
severity: CRITICAL
exit-code: 1

# Phase 2(2-3ヶ月目): HIGH追加
severity: HIGH,CRITICAL
exit-code: 1

# Phase 3(4ヶ月目以降): 必要に応じてMEDIUM追加
# ただしMEDIUMは偽陽性が多いため慎重に

2. 到達可能性分析(Reachability Analysis)の活用

SCAツールは依存パッケージに脆弱性があると報告しますが、アプリケーションが実際にその脆弱性のあるコードパスを呼び出しているかは別問題です。到達可能性分析はこの判定を自動化します。

AppSec Santaの報告では、到達可能性分析によりアラートの70-90%を非対応に分類でき、開発者の対応負荷を大幅に軽減できるとされています。

2026年時点では、到達可能性分析は主に商用ツール(Snyk、Endor Labs)で提供されており、OSSでの選択肢は限定的です。予算がある場合はSnykの導入を検討する価値があります。

3. サプレッションの運用ルール

偽陽性をサプレス(抑制)する場合は、理由と有効期限を明記します。

# .semgrep/ignores.yml
# サプレッション例: テストコード内のハードコードされた値は意図的
# 理由: テスト用のモック値であり、本番シークレットではない
# 有効期限: 2026-06-30(四半期レビューで再評価)
- pattern: tests/**
  rule: python-hardcoded-secret

よくある間違い: 「面倒だから全てサプレスする」は最悪のパターンです。四半期ごとにサプレッション一覧をレビューし、不要になったものを削除する運用ルールを設けましょう。

運用メトリクスの設計

セキュリティテスト自動化の効果を測定するには、以下のメトリクスを追跡します。

メトリクス 目標値 測定方法
パイプラインカバレッジ 全リポジトリの100% SAST/SCAが有効なリポジトリ数
MTTR(Critical) 7日以内 Issue作成からクローズまでの日数
MTTR(High) 30日以内 同上
偽陽性率 30%未満 サプレス数 / 全検出数
開発者対応率 月次で50%以上 月内にクローズされたFinding数 / 新規Finding数
スキャン時間 PRあたり60秒未満 CI/CDパイプラインのジョブ実行時間

出典: DevSecOps Implementation Guide

12週間の導入ロードマップを設計する

CORE Systems社の記事を参考に、段階的な導入計画を設計します。ツールの導入だけでなく、プロセスとオーナーシップの定義が成功の鍵です。

Phase 1: Quick Wins(1-2週目)

目標: 最小コストで即効性のあるセキュリティチェックを導入

# Step 1: GitLeaksでシークレット漏洩を防止
# pre-commitフックとして設定
pip install pre-commit

# .pre-commit-config.yaml
# repos:
#   - repo: https://github.com/gitleaks/gitleaks
#     rev: v8.21.2
#     hooks:
#       - id: gitleaks
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.2
    hooks:
      - id: gitleaks
# Step 2: Dependabotを有効化(GitHub設定画面 or dependabot.yml)
# 上述の .github/dependabot.yml を配置するだけで完了

成果指標: シークレット漏洩ゼロ、依存パッケージの脆弱性アラート受信開始

Phase 2: SAST品質ゲート(3-4週目)

目標: PRレベルでのコード品質ゲートを確立

  • Semgrepワークフローをdevelop/mainブランチのPRに設定
  • 最初の2週間は警告モードexit-code: 0)で運用し、既存コードの脆弱性を棚卸し
  • チームでルールの有効/無効を議論し、組織に合ったルールセットを確定
  • 3週目からexit-code: 1に切り替え、新規コードのみをブロック対象にする

Phase 3: SCA + コンテナスキャン(5-8週目)

目標: 依存パッケージとコンテナイメージのセキュリティを自動検証

  • TrivyによるファイルシステムスキャンとDockerイメージスキャンをCIに追加
  • 既存の脆弱性を分類し、対応優先度を設定
  • Dependabotの自動更新PRのレビュープロセスを確立

Phase 4: DAST + ランタイム監視(9-12週目)

目標: 動的テストとランタイム監視で防御層を完成

  • OWASP ZAP Baseline ScanをステージングデプロイのCIに追加
  • Full Scanを週次スケジュールで実行
  • ルールチューニングと偽陽性の棚卸し

導入でよくある失敗パターン

DevSecOps Implementation Guideでは、最も多い失敗として**「ツール導入先行、プロセス未定義」**が挙げられています。

失敗パターン 原因 対策
アラート放置 オーナーシップ未定義 Severity別にSLA(対応期限)を設定
ツール無効化 偽陽性過多で信頼喪失 偽陽性率30%を超えたらルールチューニング優先
全ツール同時導入 チームの学習コスト超過 12週間ロードマップに沿って段階的に導入
セキュリティチームだけが運用 開発者の当事者意識なし 開発者にトリアージ権限を付与、ハンズオン研修実施

よくある問題と解決方法

問題 原因 解決方法
Semgrepで大量のINFOレベルの指摘が出る デフォルトルールセットにINFOが含まれる --severity=ERROR --severity=WARNINGで絞り込み
CodeQLのスキャンに30分以上かかる リポジトリが大きい、またはコンパイル言語が多い PR時はSemgrep、CodeQLは週次スケジュールに分離
TrivyがCRITICALを検出してCIが止まる 既存の脆弱性が多数存在 exit-code: 0で2-4週間運用後、段階的に閾値を上げる
OWASP ZAPがタイムアウトする Full Scanがステージングサーバーに過負荷 Baseline Scan(パッシブ)のみCIに含め、Full Scanは週次
Dependabot PRが多すぎてレビューが追いつかない 個別パッケージごとにPRが作成される groups設定でPRを集約、またはRenovateに移行

まとめと次のステップ

まとめ:

  • SAST(ソースコード解析)、DAST(動的テスト)、SCA(依存パッケージ分析)は互いの検出の死角を補完する関係にあり、3層すべてをCI/CDに統合するのが2026年のDevSecOps標準アプローチ
  • Semgrep(PR高速スキャン10秒)+ CodeQL(週次ディープ分析)の併用パターンがLinkedIn等の大規模組織で採用されている
  • ツールの導入よりも、**偽陽性管理(閾値の段階的引き上げ、到達可能性分析、サプレッションルール)プロセス設計(SLA、オーナーシップ、メトリクス)**が成功の鍵
  • 段階的に12週間のロードマップで導入し、Quick Wins(シークレット検出+Dependabot)から始めてDASTまで拡張するのが実践的

次にやるべきこと:

  1. 今日: .pre-commit-config.yamlにGitLeaksを追加してシークレット漏洩を防止する
  2. 今週: .github/dependabot.ymlを作成してSCAの自動化を開始する
  3. 来週: Semgrepのワークフローを警告モードでCIに追加し、既存コードの脆弱性を棚卸しする

関連記事として、コンテナイメージスキャンの詳細は「TrivyとGrypeで始めるコンテナイメージスキャンとCI/CDセキュリティ実践ガイド」も参照してください。

参考


注意: この記事はAI(Claude Code)により自動生成されました。内容の正確性については複数の情報源で検証していますが、実際の利用時は公式ドキュメントもご確認ください。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?