Rails 5.2: Best practice for setting CSP nonce - ruby-on-rails

Our application is on Rails 5.2 and it is serving assets with webpacker without the asset pipeline. I was wondering what is the best way to set the nonce attributes on the script tag.
In content_security_policy.rb, there is a content_security_policy_nonce_generator for UJS, I was wondering if I can still use that without any side effect. The following work and I was just wondering what is the best practice for doing something like this.
#initializers/content_security_policy.rb
# If you are using UJS then enable automatic nonce generation
Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16)
In application.html.erb, if I want to have nonce on the script tag, I will have to get it from the request. According here: https://api.rubyonrails.org/classes/ActionDispatch/ContentSecurityPolicy/Request.html#method-i-content_security_policy_nonce
#app/views/layouts/application.html.erb
<!DOCTYPE html>
<html dir="ltr">
<head>
<title>FruitsMarket</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_pack_tag 'application' %>
<%= javascript_pack_tag 'polyfills' %>
<%= javascript_pack_tag 'application' %>
<script type="text/javascript" nonce=<%= request.content_security_policy_nonce %>>
alert('hi');
</script>
</head>
<body>
<%= yield %>
</body>
</html>

Found it https://api.rubyonrails.org/classes/ActionView/Helpers/JavaScriptHelper.html
turns out there is a ruby view helper for that
<%= javascript_tag nonce: true do -%>
alert('All is good')
<% end -%>

Related

How can I use yield :content before content_for in Rails templates?

I need to use yield/content_for in <head>, but assign the value in <body>. I have found that this works fine when the value is being assigned from within a template that is being yielded to, but not one that is being rendered. Templates that are being rendered are compiled after <head>, so my value is already set in stone. Is there a way to achieve what I am trying to do?
I tried making application.html.erb look like this:
<%= render layout: 'application_template' do %>
<!-- <body> content here -->
<% end %>
and _application_template.html.erb look like:
<!doctype>
<html>
<head>
<%= content_for :my_value %>
</head>
<body>
<%= yield %>
</body
</html>
but the same problem happens, the value is nil when _application_template.html.erb is rendered.
You should be able to do the following.
In your head check if there is any content to yield and yield it if it is.
<% if content_for?(:my_value) %>
<%= yield :my_value %>
<% else %>
And then define your content somewhere in your body.
<% content_for :my_value do %>
# your contents
<% end %>
I figured out a solution.
# layouts/application.html.erb
<% content_for(:body_content) { render partial: 'layouts/body_content' } %>
<!doctype html>
<html>
<head>
<%= yield :my_value if content_for?(:my_value) %>
</head>
<body>
<%= yield :body_content %>
</body>
</html>
and
# layouts/_body_content.html.erb
# everything that used to be in <body></body> goes here and is maintained here.
This makes sure that all of the #content_for calls I need to happen will happen before <head> is compiled in my layout. It is also easier to maintain than some of the other hacks I had thought of trying.

Bootstrap not working on Heroku app

My rails app is styled with Bootstrap delivered from a CDN. It works fine in development but the styling is lost after uploading to Heroku. I don't have any bootstrap gems in the gemfile. Here is a screen shot of my appplication.html.erb file.
<!DOCTYPE html>
<html>
<head>
<title><%= page_title %></title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</head>
<body>
<%= render "layouts/header" %>
<%= flash[:notice] %>
<%= flash[:alert] %>
<div id="main">
<%= render "layouts/left_sidebar" %>
<div id="body"><%= yield %></div>
<%= render "layouts/right_sidebar" %>
</div>
</body>
</html>
I am not sure about the info inside the head tags, maybe it is incorrect or should I add a gem? I am not sure what to do. Please help. Thanks in advance.
Not sure if it's causing the problem, but the protocol for one of your Bootstrap links is http and the other is https. You might check your console for load errors.
Since you mentioned the gem, adding this to your Gemfile should do the trick:
gem 'anjlab-bootstrap-rails', '~> 3.0.0.3', :require => 'bootstrap-rails'
I have three apps on Heroku running Bootstrap with this gem and it has never given me any trouble.

Rails 4 TurboLinks not disabling from link in layout

I have a rails 4 project that has some public routes (home, contact, about) and then some routes that are only for logged in users that are name spaced under admin (admin/home, admin/contact, admin/about). The admin routes basically edit and update what is displayed in the public routes.
I have an admin layout for the admin routes and a application layout which does the normal public routes. My admin layout has to have data-no-turbolink in the body because I NEED to disable turbolinks in order for the CKEditor gem to work properly.
Each layout has a link that shows up if a User is logged in. So if a user is logged in and visits "localhost:3000/" or "http://localhost:3000/contact" etc, there will be a link that will link to the admin page.
Likewise if a logged in user visits "http://localhost:3000/admin/home" or "http://localhost:3000/admin/contact" there will be a link that links to the public page. Here is what the layouts look like.
ADMIN LAYOUT
<!DOCTYPE html>
<html>
<head>
<title>TITLE</title>
<script src="//cdn.ckeditor.com/4.4.6/basic/ckeditor.js"></script>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => false %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => false %>
<%= csrf_meta_tags %>
</head>
<body data-no-turbolink>
<div class="page-wrap">
<header id="main-head">
<%= render "nav"%>
</header>
<% if logged_in %>
<div id="review-default"><%= link_to "Preview Pages As A Regular User.", root_path %></div>
<% end %>
<%= yield %>
</div>
</body>
</html>
APPLICATION LAYOUT
<!DOCTYPE html>
<html>
<head>
<title>TITLE</title>
<script src="//cdn.ckeditor.com/4.4.6/basic/ckeditor.js"></script>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<div class="page-wrap">
<header id="main-head">
<%= render "nav"%>
</header>
<% if logged_in %>
<div id="review-admin"><%= link_to "Return to Admin Panel.", admin_home_path %></div>
<% end %>
<%= yield %>
</div>
</body>
</html>
My problem is that when I click from say the root path "/" to "admin/home" the forms that CKEditor is applied to dont display properly until I refresh the page, which leads me to think that they are not displaying properly because turbo links are not disabled.
But when I, for example, log in and get directed to the adminroutes they are disabled. But they dont seem to be when I am linked to them from the links in the layout...
I hope that makes sense.
What am i doing wrong and how do I stop turbo links from being applied to my admin routes?
EDIT
my solution was to add
<% if logged_in %>
<body data-no-turbolink>
<% else %>
<body >
<% end %>
to both my admin and application layouts. This solves my problem, but if anyone has a more elegant solution (since if the admin layout is being called correctly by having layout "admin" in my admin controller it doesn't seem like I would have to do this), I am all ears!
BTW, I love you StackOverFlow. Thank you for all your patience and help.
my solution was to add
<% if logged_in %>
<body data-no-turbolink>
<% else %>
<body >
<% end %>
to both my admin and application layouts. This solves my problem, but if anyone has a more elegant solution (since if the admin layout is being called correctly by having layout "admin" in my admin controller it doesn't seem like I would have to do this), I am all ears!

Is <%= yield %> the Rails equivalent of MVC3's RenderContent()?

I'm following the Ruby of Rails getting started guide, and I see this code in the layout file:
<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body style="background: #EEEEEE;">
<%= yield %>
</body>
</html>
Coming from an MVC3 background, is this the equivalent to the RenderContent() method one would invoke from the _layout.cshtml file?
The functionality is about the same in that context, yes. However, yield in general is a keyword in the ruby language, concerning blocks. You can find more information here: ruby blocks.
Building on that, you are able to provide content for different parts, using content_for(:something) and yield :something (the yield passes :something to the layout engine, the layout engine fills in the content for it).

the best way to have separate stylesheets for different actions in Rails

I have one app layout that includes app wide css stylesheets and js files, but some controller's actions need additional stylesheets that is used only for these action
s view? What is the best way to include them?
If your app is typically something like:
<html>
<head>
<%= stylesheet_link_tag 'application' -%>
<%= javascript_include_tag 'yui', 'application' -%>
</head>
<body>
<%= yield -%>
</body>
</html>
You can add other yield blocks wherever you like, named whatever you want. Typically I used this to include page-specific functionality wherever I like, even to the degree that maybe partials supply their own.
# layouts/application.html.erb
<html>
<head>
<%= stylesheet_link_tag 'application' -%>
<%= javascript_include_tag 'yui', 'application' -%>
<%= yield :head -%>
</head>
<body>
<%= yield -%>
</body>
</html>
# views/profiles/show.html.erb
<%= title("#{#user.name}'s Profile") -%>
<% content_for :head do -%>
<%= javascript_include_tag 'gallery' %>
<% end %>
<%= render #user.photos %>
So on and so forth...
For the javascript, one way to deal with this is using a <%= yield :javascript %> inside your the ... tag in your layout file. Then in your view code, you can use a <% content_for :javascript do %> ...put your javascript here ... <% end %>
This way, your javascript and your html erb code live in the same location.
Javascript that is used throughout the sight can be placed in a global file.

Resources