defmodule Metastatic.Analysis.BusinessLogic.InlineJavascript do @moduledoc """ Detects inline executable code in templates/strings (XSS/injection risk). Universal pattern: embedding code directly in strings/templates without sanitization. ## Examples **Python (Django template with unsafe JS):** ```python html = f'' # XSS risk - unescaped data ``` **JavaScript (React with dangerouslySetInnerHTML):** ```javascript return
; # XSS vulnerability ``` **Elixir (Phoenix template with raw JS):** ```elixir ~H\"\"\" \"\"\" ``` **C# (ASP.NET with Html.Raw):** ```csharp @Html.Raw($\"\") # XSS risk ``` **Go (template with unescaped JS):** ```go tmpl := template.Must(template.New(\"page\").Parse( `` # Should use JS escaping )) ``` **Java (JSP with script tag):** ```java out.println(\"\"); # XSS vulnerability ``` **Ruby (Rails with javascript_tag):** ```ruby javascript_tag "var userId = " + user_id.to_s # Should use escape_javascript ``` **PHP (inline script without escaping):** ```php echo \"\"; # XSS risk - use htmlspecialchars ``` """ @behaviour Metastatic.Analysis.Analyzer alias Metastatic.Analysis.Analyzer @dangerous_patterns [ "", "dangerouslysetinnerhtml", "html.raw", "javascript:", "onclick=", "onerror=" ] @impl true def info do %{ name: :inline_javascript, category: :security, description: "Detects inline JavaScript/code in strings (XSS risk)", severity: :error, explanation: "Avoid inline scripts - use CSP-compliant external scripts or proper escaping", configurable: true } end @impl true def analyze({:literal, meta, content} = node, _context) when is_list(meta) and is_binary(content) do # Check if this is a string literal in 3-tuple format if Keyword.get(meta, :subtype) == :string do content_lower = String.downcase(content) if Enum.any?(@dangerous_patterns, &String.contains?(content_lower, &1)) do [ Analyzer.issue( analyzer: __MODULE__, category: :security, severity: :error, message: "Inline JavaScript in string literal - XSS vulnerability", node: node, metadata: %{ pattern: "inline_script", suggestion: "Use external scripts, CSP, or proper escaping/sanitization" } ) ] else [] end else [] end end def analyze({:function_call, meta, args} = node, _context) when is_list(meta) do fn_name = Keyword.get(meta, :name, "") fn_lower = String.downcase(fn_name) # Check for dangerous functions if String.contains?(fn_lower, [ "dangerouslysetinnerhtml", "html.raw", "javascript_tag" ]) do [ Analyzer.issue( analyzer: __MODULE__, category: :security, severity: :error, message: "Using '#{fn_name}' to inject HTML/JS - potential XSS vulnerability", node: node, metadata: %{ function: fn_name, suggestion: "Sanitize content or use framework's safe rendering methods" } ) ] else # Check if args contain script tags if has_script_in_args?(args) do [ Analyzer.issue( analyzer: __MODULE__, category: :security, severity: :warning, message: "Function call contains inline script - verify proper escaping", node: node, metadata: %{ suggestion: "Ensure all user data is properly escaped" } ) ] else [] end end end def analyze(_node, _context), do: [] defp has_script_in_args?(args) when is_list(args) do Enum.any?(args, fn {:literal, meta, content} when is_list(meta) and is_binary(content) -> Keyword.get(meta, :subtype) == :string and String.contains?(String.downcase(content), [""]) _ -> false end) end defp has_script_in_args?(_), do: false end