Ragex.Analysis.BusinessLogic (Ragex v0.10.0)

View Source

Business logic analysis using Metastatic analyzers.

Provides unified access to 33 language-agnostic business logic analyzers that detect common anti-patterns, security vulnerabilities, and issues across multiple languages.

Semantic Analysis with OpKind

Many analyzers leverage Metastatic's OpKind semantic metadata system for accurate detection. OpKind tags function calls with their semantic meaning:

  • Domain: :db, :http, :auth, :cache, :queue, :file, :external_api
  • Operation: :retrieve, :create, :update, :delete, :query, etc.
  • Framework: :ecto, :django, :activerecord, :requests, etc.

Location Information

Note: Business logic analyzers operate at the MetaAST (M2) abstraction level, which intentionally abstracts away language-specific details like line numbers. This is what makes them language-agnostic. As a result, precise line/column location information is typically not available. Issues will include file paths and function context when available.

Analyzers

Tier 1: Pure MetaAST (Language-Agnostic)

  • CallbackHell - Detects deeply nested conditionals (M2.1 Core)
  • MissingErrorHandling - Pattern matching without error case (M2.2 Extended)
  • SilentErrorCase - Conditionals with only success path (M2.1 Core)
  • SwallowingException - Exception handling without logging (M2.2 Extended)
  • HardcodedValue - Hardcoded URLs/IPs in literals (M2.1 Core)
  • NPlusOneQuery - DB queries in collection operations (M2.2 Extended)
  • InefficientFilter - Fetch-all then filter pattern (M2.2 Extended)
  • UnmanagedTask - Unsupervised async operations (M2.2 Extended)
  • TelemetryInRecursiveFunction - Metrics in recursive functions (M2.1 Core)

Tier 2: Function Name Heuristics

  • MissingTelemetryForExternalHttp - HTTP calls without telemetry
  • SyncOverAsync - Blocking operations in async contexts
  • DirectStructUpdate - Struct updates bypassing validation
  • MissingHandleAsync - Unmonitored async operations

Tier 3: Naming Conventions

  • BlockingInPlug - Blocking I/O in middleware
  • MissingTelemetryInAuthPlug - Auth checks without audit logging
  • MissingTelemetryInLiveviewMount - Component lifecycle without metrics
  • MissingTelemetryInObanWorker - Background jobs without telemetry

Tier 4: Content Analysis

  • MissingPreload - Database queries without eager loading
  • InlineJavascript - Inline scripts in strings (XSS risk)
  • MissingThrottle - Expensive operations without rate limiting

Tier 5: Security (CWE-based)

  • SQLInjection - SQL query string concatenation (CWE-89)
  • XSSVulnerability - Cross-site scripting risks (CWE-79)
  • SSRFVulnerability - Server-side request forgery (CWE-918)
  • PathTraversal - Directory traversal attacks (CWE-22)
  • InsecureDirectObjectReference - IDOR vulnerabilities (CWE-639)
  • MissingAuthentication - Unprotected endpoints (CWE-306)
  • MissingAuthorization - Missing access control (CWE-862)
  • IncorrectAuthorization - Flawed access control (CWE-863)
  • MissingCSRFProtection - CSRF vulnerabilities (CWE-352)
  • SensitiveDataExposure - Data leaks in logs/responses (CWE-200)
  • UnrestrictedFileUpload - Unsafe file uploads (CWE-434)
  • ImproperInputValidation - Input validation issues (CWE-20)

Tier 6: Race Conditions

  • TOCTOU - Time-of-check-time-of-use vulnerabilities (CWE-367)

Usage

alias Ragex.Analysis.BusinessLogic

# Analyze single file
{:ok, result} = BusinessLogic.analyze_file("lib/my_module.ex")

# Check for issues
result.has_issues?     # => true/false
result.total_issues    # => 5
result.critical_count  # => 1

# Analyze directory
{:ok, results} = BusinessLogic.analyze_directory("lib/")

# Run specific analyzers
{:ok, result} = BusinessLogic.analyze_file("lib/my_module.ex",
  analyzers: [:callback_hell, :missing_error_handling])

# Filter by severity
{:ok, results} = BusinessLogic.analyze_directory("lib/",
  min_severity: :high)

# Generate report
report = BusinessLogic.audit_report(results)

Summary

Functions

Analyzes all files in a directory for business logic issues.

Analyzes a single file for business logic issues.

Generates a comprehensive business logic audit report.

Returns the list of available business logic analyzers.

Returns the recommendation for a specific analyzer.

Types

analysis_result()

@type analysis_result() :: %{
  file: String.t(),
  language: atom(),
  issues: [issue()],
  has_issues?: boolean(),
  total_issues: non_neg_integer(),
  critical_count: non_neg_integer(),
  high_count: non_neg_integer(),
  medium_count: non_neg_integer(),
  low_count: non_neg_integer(),
  info_count: non_neg_integer(),
  by_analyzer: %{required(atom()) => non_neg_integer()},
  timestamp: DateTime.t()
}

directory_result()

@type directory_result() :: %{
  total_files: non_neg_integer(),
  files_with_issues: non_neg_integer(),
  total_issues: non_neg_integer(),
  by_severity: %{required(atom()) => non_neg_integer()},
  by_analyzer: %{required(atom()) => non_neg_integer()},
  results: [analysis_result()],
  summary: String.t()
}

issue()

@type issue() :: %{
  analyzer: atom(),
  category: atom(),
  severity: :critical | :high | :medium | :low | :info,
  description: String.t(),
  suggestion: String.t() | nil,
  context: map(),
  location: location() | nil
}

location()

@type location() :: %{
  line: non_neg_integer() | nil,
  column: non_neg_integer() | nil,
  function: String.t() | nil
}

Functions

analyze_directory(path, opts \\ [])

@spec analyze_directory(
  String.t(),
  keyword()
) :: {:ok, directory_result()} | {:error, term()}

Analyzes all files in a directory for business logic issues.

Options

  • :recursive - Recursively analyze subdirectories (default: true)
  • :parallel - Use parallel processing (default: true)
  • :max_concurrency - Maximum concurrent analyses (default: System.schedulers_online())
  • Plus all options from analyze_file/2

Examples

{:ok, results} = BusinessLogic.analyze_directory("lib/")
total_issues = results.total_issues

analyze_file(path, opts \\ [])

@spec analyze_file(
  String.t(),
  keyword()
) :: {:ok, analysis_result()} | {:error, term()}

Analyzes a single file for business logic issues.

Options

  • :analyzers - List of analyzer names to run (default: all)
  • :language - Explicit language (default: auto-detect)
  • :min_severity - Minimum severity to report (default: :info)
  • :config - Configuration map for analyzers

Examples

{:ok, result} = BusinessLogic.analyze_file("lib/my_module.ex")
result.has_issues?  # => false

{:ok, result} = BusinessLogic.analyze_file("lib/my_module.ex",
  analyzers: [:callback_hell, :missing_error_handling],
  min_severity: :high)

audit_report(results)

@spec audit_report([analysis_result()]) :: map()

Generates a comprehensive business logic audit report.

Returns a formatted map with:

  • Summary statistics
  • Issues grouped by severity
  • Issues grouped by analyzer
  • Recommendations

Examples

{:ok, results} = BusinessLogic.analyze_directory("lib/")
report = BusinessLogic.audit_report(results.results)
IO.puts(report.summary)

available_analyzers()

@spec available_analyzers() :: [atom()]

Returns the list of available business logic analyzers.

Examples

iex> Ragex.Analysis.BusinessLogic.available_analyzers()
[:callback_hell, :missing_error_handling, ...]

recommendation(analyzer)

@spec recommendation(atom()) :: String.t()

Returns the recommendation for a specific analyzer.

For security analyzers, includes CWE reference numbers.

Examples

iex> Ragex.Analysis.BusinessLogic.recommendation(:sql_injection)
"CWE-89: Use parameterized queries or Ecto's query DSL..."