# frozen_string_literal: true module DiscourseAi module AiBot class ArtifactsController < ApplicationController requires_plugin DiscourseAi::PLUGIN_NAME before_action :require_site_settings! skip_before_action :preload_json, :check_xhr, only: %i[show] def show artifact = AiArtifact.find(params[:id]) post = Post.find_by(id: artifact.post_id) if artifact.public? # no guardian needed else raise Discourse::NotFound if !post&.topic&.private_message? raise Discourse::NotFound if !guardian.can_see?(post) end name = artifact.name artifact_version = nil if params[:version].present? artifact_version = artifact.versions.find_by(version_number: params[:version]) raise Discourse::NotFound if !artifact_version end untrusted_html = build_untrusted_html(artifact_version || artifact, name) trusted_html = build_trusted_html(artifact, artifact_version, name, untrusted_html) set_security_headers render html: trusted_html.html_safe, layout: false, content_type: "text/html" end private def build_untrusted_html(artifact, name) js = prepare_javascript(artifact.js) <<~HTML #{ERB::Util.html_escape(name)} #{build_iframe_javascript} #{artifact.html} #{js} HTML end def build_trusted_html(artifact, artifact_version, name, untrusted_html) <<~HTML #{ERB::Util.html_escape(name)} #{build_parent_javascript(artifact)} HTML end def prepare_javascript(js) return "" if js.blank? if !js.match?(%r{\A\s*}mi) mod = "" mod = " type=\"module\"" if js.match?(/\A\s*import.*/) js = "\n#{js}\n" end js end def user_data { username: current_user ? current_user.username : nil, user_id: current_user ? current_user.id : nil, name: current_user ? current_user.name : nil, } end def build_iframe_javascript <<~JAVASCRIPT JAVASCRIPT end def build_parent_javascript(artifact) <<~JAVASCRIPT JAVASCRIPT end def set_security_headers response.headers.delete("X-Frame-Options") response.headers[ "Content-Security-Policy" ] = "script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' #{AiArtifact::ALLOWED_CDN_SOURCES.join(" ")};" response.headers["X-Robots-Tag"] = "noindex" end def require_site_settings! if !SiteSetting.discourse_ai_enabled || !SiteSetting.ai_artifact_security.in?(%w[lax hybrid strict]) raise Discourse::NotFound end end end end end