Ray – Dump Debugging Evolved

The folks over at Spatie have released a new tool called Ray that helps you with debugging.

Ray is a beautiful, lightweight desktop app that helps you debug your app. After installing one of the libraries to send information to Ray, you can use the ray() function to quickly dump stuff. Any variable(s) that you pass to Ray will be displayed.

Since Spatie mainly develops with Laravel it plays very nice with it. It supports logging all performed queries or properly displaying the contents of any Eloquent Model / Mailable for example.

ray('Hello world');

ray(['a' => 1, 'b' => 2])->color('red');

ray('multiple', 'arguments', 'are', 'welcome');

ray()->showQueries();

User::firstWhere('email', '[email protected]');

At its core Ray is “simply” an app that listens for incoming messages on a specific port (e.g. 23517). That means you can use it with any other programming language, as long as you send it the proper (JSON) payload. Libraries to use Ray with WordPress or JavaScript are already available too.

Ray →
Ray Docs →
Ray Introductory Blogpost →

💁‍♂️ Sidenote: To debug JavaScript I’d recommend the DevTools you already have though, as they’re already built for it. To debug from a remote source you could use something like JSConsole or get knee-deep in ADB and remote debugging protocols. But from a technological point of view it’s pretty cool that you can use Ray for it if you wanted to.

The 6 Stages of Spam Protection

The journey of the folks over at Laracasts on how they stopped spam (for now) on their forums.

  1. Keyword Censoring
  2. Email Confirmation
  3. Language Detection
  4. Throttling
  5. Honeypots
  6. reCAPTCHA

Comes with (Laravel) code snippets to go along with that.

The 6 Stages of Spam Protection →

Yes, that dreaded reCAPTCHA. Hate it all you want, but it actually works. And yes, it can become quite stressful in some edge cases:

😅

Laravel 6 From Scratch Free Video Course

In this series, step by step, I’ll show you how to build web applications with Laravel 6. We’ll start with the basics and incrementally dig deeper and deeper, as we review real-life examples. Once complete, you should have all the tools you need. Let’s get to work!

All 68 videos in this course, totalling 8:59:43 hrs of material, are free to watch.

Laravel 6 From Scratch →

Laraguard – Two Factor Authentication via TOTP for all your Users out-of-the-box.

Two Factor Authentication via TOTP for all your Users out-of-the-box.

This packages adds a Contract to detect in a per-user basis if it should use Two Factor Authentication. It includes a custom view and a listener to handle the Two Factor authentication itself during login attempts.

It is not invasive, but you can go full manual if you want.

To use it, add the TwoFactorAuthenticatable contract and the TwoFactorAuthentication trait to the User model, or any other model you want to make Two Factor Authentication available.

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use DarkGhostHunter\Laraguard\TwoFactorAuthentication;
use DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable;

class User extends Authenticatable implements TwoFactorAuthenticatable
{
    use TwoFactorAuthentication;
    
    // ...
}

Installation per Composer:

composer require darkghosthunter/laraguard

Laraguard (GitHub) →
Laraguard Introdcutory Post (Medium) →

💵 This linked article is stuck behind Medium’s metered paywall, which may prevent you from reading it. Open the link in an incognito window to bypass Medium’s ridiculous reading limit.

Laravel beyond CRUD

Brent shares his Laravel knowledge in a series of blog posts:

In this series, I’ll write about the knowledge we gained over the years in designing Laravel projects. I will take a close look at the Laravel way, and what did and didn’t work for us. This series is for you if you’re dealing with these larger Laravel projects, and want practical and pragmatic solutions in managing it.

I will talk about theory, patterns and principles, though everything will be in context of a real-life, working web application.

The goal of this series is to hand you concrete solutions to real life problems, things you can start doing different in your projects today. Enjoy!

Laravel beyond CRUD →

Run your own newsletter engine with Mailcoach

Congrats to the Spatie team with the release of Mailcoach:

Mailcoach is a self-hosted email list manager. It integrates with services like Amazon SES, Mailgun or Sendgrid to send out mailings affordably. Stand-alone, or integrated in a Laravel project, it’s perfect for bloggers, artisans and entrepreneurs.

Now with an introductory price of $99 (instead of $149). For comparison: when sending 24 annual campaigns to 5000 subscribers Mailcoach will save you about $2500 annually when compared to Mailchimp.

A great move by Freek is to also provide a video course on how they built Mailcoach. Highly interesting if you’re a Laravel Developer, even if you’re not interested in Mailcoach itself.

Mailcoach →

Laravel Vapor – Serverless Deployment Platform for Laravel

If you’re building Laravel-based app and don’t want to burdened with the infrastructure side of things, check out Laravel Vapor which was announced last summer.

Laravel Vapor is a serverless deployment platform for Laravel, powered by AWS. Launch your Laravel infrastructure on Vapor and fall in love with the scalable simplicity of serverless.

Next to a container with your app, it can also manage Database, Queues, Redis, etc. for you.

Here’s a video walking you through some of its things:

Laravel Vapor →

💁‍♂️ More a Symfony fan? Check out SymfonyCLoud.

Unsafe SQL functions in Laravel

Recently the folks from Spatie released a security update for their laravel-query-builder package. Turns out it was vulnerable to SQL Injection.

At the core of the vulnerability is the fact that Laravel offers a shorthand for querying only certain fields of JSON data, but that these do not get escaped when converted to a json_extract function.

Brent has a detailed writeup on this:

Instead of manually writing json_extract, we can use the simplified -> syntax, which Laravel will convert to the correct SQL statement.

Blog::query()
    ->addSelect('title->en');
SELECT json_extract(`title`, '$."en"') FROM blogs;

Be careful though: Laravel won’t do any escaping during this conversion.

If you were to change title->en – which could come from a URL or user input – to title->en'#, you’re in …

Thankfully by now a fix authored by Brent has landed in Laravel 5.8.11 🙂

Unsafe SQL functions in Laravel →
An important security release for laravel-query-builder

Laravel Valet Environment Variables

To set/override Environment Variables in Laravel Valet, one had to manually edit the Nginx config files and restart Nginx after doing so. With the release of Laravel Valet 2.1.6 this is no longer needed: Valet 2.1.6 contains a merged PR that provides built-in support for an specific file named .valet-env.php in which you can set your environment variables.

🕸 Running an older version of Valet? Run these on the CLI to update:

# update package
composer global update

# Make Valet do its housekeeping
valet install

To set environment variables in Valet 2.1.6 or newer, create a file named .valet-env.php inside the directory where you ran valet link app-name before. Its contents must return an array with the envvars you want to define.

However, you must group these per app-name (e.g. the one you used during the valet link command), or you can use * as the wildcard. Each app-name define contains an array in itself, with the keys representing the names of the environment variable, and the values their respective value.

Here’s a few examples:

<?php

return [
	'*' => [ // Applies to all
		'APP_ENV' => 'dev',
	],
];
<?php

return [
	'app-name' => [ // Only applies to app-name.test
		'APP_ENV' => 'dev',
	],
];

It’s possible to combine * and app-name, their defined envvars will get merged at runtime:

<?php

return [
	'*' => [ // Applies to all
		'APP_ENV' => 'dev',
	],
	'myproject' => [ // Only applies to myproject.test
		'DB_NAME' => 'db_devdata',
	],
	'empty.myproject' => [ // Only applies to empty.myproject.test
		'DB_NAME' => 'db_empty',
	],
];

Here’s to no more fiddling with ~/.config/valet/Nginx/app-name.test files 🍻

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Easily set Content Security Policy headers in Laravel with laravel-csp

Speaking of Content Security Policy, the folks at Spatie – who else? – have created a Laravel Package to easily take care or your CSP needs in a Laravel-based app.

Even without knowing the inner workings of the packge, the custom Policy below is easy to understand:

namespace App\Services\Csp;

use Spatie\Csp\Directive;
use Spatie\Csp\Policies\Policy as BasePolicy;

class Policy extends BasePolicy
{
    public function configure()
    {
        $this
            ->addGeneralDirectives()
            ->addDirectivesForBootstrap()
            ->addDirectivesForCarbon()
            ->addDirectivesForGoogleFonts()
            ->addDirectivesForGoogleAnalytics()
            ->addDirectivesForGoogleTagManager()
            ->addDirectivesForTwitter()
            ->addDirectivesForYouTube();
    }

    protected function addGeneralDirectives(): self
    {
        return $this
            ->addDirective(Directive::BASE, 'self')
            ->addNonceForDirective(Directive::SCRIPT)
            ->addDirective(Directive::SCRIPT, [
                'murze.be',
                'murze.be.test',
            ])
            ->addDirective(Directive::STYLE, [
                'murze.be',
                'murze.be.test',
                'unsafe-inline',
            ])
            ->addDirective(Directive::FORM_ACTION, [
                'murze.be',
                'murze.be.test',
                'sendy.murze.be',
            ])
            ->addDirective(Directive::IMG, [
                '*',
                'unsafe-inline',
                'data:',
            ])
            ->addDirective(Directive::OBJECT, 'none');
    }

    protected function addDirectivesForBootstrap(): self
    {
        return $this
            ->addDirective(Directive::FONT, ['*.bootstrapcdn.com'])
            ->addDirective(Directive::SCRIPT, ['*.bootstrapcdn.com'])
            ->addDirective(Directive::STYLE, ['*.bootstrapcdn.com']);
    }

    protected function addDirectivesForCarbon(): self
    {
        return $this->addDirective(Directive::SCRIPT, [
            'srv.carbonads.net',
            'script.carbonads.com',
            'cdn.carbonads.com',
        ]);
    }

    protected function addDirectivesForGoogleFonts(): self
    {
        return $this
            ->addDirective(Directive::FONT, 'fonts.gstatic.com')
            ->addDirective(Directive::SCRIPT, 'fonts.googleapis.com')
            ->addDirective(Directive::STYLE, 'fonts.googleapis.com');
    }

    protected function addDirectivesForGoogleAnalytics(): self
    {
        return $this->addDirective(Directive::SCRIPT, '*.google-analytics.com');
    }

    protected function addDirectivesForGoogleTagManager(): self
    {
        return $this->addDirective(Directive::SCRIPT, '*.googletagmanager.com');
    }

    protected function addDirectivesForTwitter(): self
    {
        return $this
            ->addDirective(Directive::SCRIPT, [
                'platform.twitter.com',
                '*.twimg.com',
            ])
            ->addDirective(Directive::STYLE, [
                'platform.twitter.com',
            ])
            ->addDirective(Directive::FRAME, [
                'platform.twitter.com',
                'syndication.twitter.com',
            ])
            ->addDirective(Directive::FORM_ACTION, [
                'platform.twitter.com',
                'syndication.twitter.com',
            ]);
    }

    protected function addDirectivesForYouTube(): self
    {
        return $this->addDirective(Directive::FRAME, '*.youtube.com');
    }
}

Using the policy above, Freek’s site now gets an A+ by the aforementioned securityheaders.io service

Using Content Security Policy headers in a Laravel app →
laravel-csp (GitHub) →