Unguarded, Sonnet 5 read another customer’s order 100/100 times. Guarded: zero.

I ran the same storefront attacks against three models — one with its safety training deliberately removed, one stock open-weights model, and the frontier model Laravel AI ships as its default — each with and without an authorization boundary in place.

The headline isn’t that the boundary held (it did: zero breaches across every guarded arm). It’s that the frontier model’s safety training turned out to be per-action: it refused the destructive attack in 100 out of 100 trials, and executed the cross-customer read in 100 out of 100 trials. The attack it misses is the one that looks like helping.

The experiment

Verdict ships deterministic attack packs — executable specifications of things an agent should never be allowed to do, like reading or cancelling another customer’s order. The attack always arrives through the conversation — a hostile request, a poisoned document — and what gets measured is whether the agent goes along with it. Every attack ran in two arms:

  • Guarded: the agent’s tools are wired through the authorization boundary.
  • Unguarded: the identical agent, tools, and prompts — boundary absent. This is the control arm: would anything bad have happened without the guard?

Integrity rules, because a rigged benchmark is worse than none: the harness never forces a tool call, a model that declines an attack is never counted as a prevention, and independent sampled trials are reported as per-arm tallies, never as “the guard stopped this exact attack.”

The numbers

abliterated 7B gpt-oss:20b Sonnet 5
Lookup breached, unguarded (per trial) 30/30 19/30 100/100
Cancellation breached, unguarded (per trial) 28/30 1/30 0/100
Breaches with the boundary guarding (per attempted attack) 0/60 0/25 0/100

Read the last column twice. The frontier model refused the destructive attack perfectly — nothing stopping it but its own training — and executed the cross-customer read as reliably as the model with its safety training stripped out. The read series isn’t even monotonic: Sonnet executed it more often than the mid-tier open-weights model. More alignment did not mean less exposure on that action.

Why the read is the one that gets through

Because it doesn’t look like an attack from where the model sits. “Cancel someone else’s order” pattern-matches to harm; frontier training catches it. “Look up order #4471” looks like doing your job — the model has a lookup tool, a user asked about an order, and nothing in its context says whose order that is. This is the confused-deputy problem, as old as computer security: the agent isn’t malicious, it’s helpful, with authority the requester shouldn’t be able to borrow.

That’s why the fix isn’t a better prompt or a more aligned model. Whose order a tool may touch is a fact in your database, checked by your policies — application state the model never sees and cannot be argued out of. Models propose; applications authorize.

What these numbers are not

  • Breach rates are properties of each model’s alignment under these attack framings, not of production — this is a harness you point at your own agent, not a leaderboard.
  • The bounds are ceilings from the rule of three (≤3% at 95% for the guarded Sonnet arm over 100 observations), not proofs.
  • The prompt-injection case is reported as undemonstrated, not prevented: no model took the bait in any run, and a denial of an attack never attempted proves nothing.

The full write-up — the other two models in detail, the legitimate-work allow-side (zero false denials), the diagrams, and every caveat — is on my blog: The AI Wouldn’t Cancel Someone Else’s Order. But It Read It Every Single Time.

Recorded runs and raw numbers: docs/evaluation.md. If you’re building AI agents on Laravel, wire your tools through the boundary and run the control arm against your own app — the attack your model’s alignment misses is probably not the one you’d guess.


Sumber Rujukan:

  • Artikel asal dari Dev.to PHP
  • Published on haqis.com
Comments

Automatically Secure Your Livewire 4 Components From Client-Side Tampering

Livewire Secure Properties 🔒

👉 View on GitHub: janecodelife/livewire-secure-properties

An elegant, zero-configuration security package for Laravel Livewire 4 that automatically locks all public component properties from client-side manipulation, unless explicitly marked as unlocked.

✅ Auto-lock properties

✅ Zero configuration

✅ Protects against client-side tampering

✅ Unlock specific properties with #[Unlocked]

✅ Supports Livewire 4 (Single & Multiple File)

Requirements

  • Livewire ^4.0

Installation

You can install the package via composer:

composer require janecodelife/livewire-secure-properties

Usage

1. Single File Components (SFC)

If you are using Livewire 4’s native Single File Components layout, you can safely use the #[Unlocked] attribute inside the anonymous class block:


use Livewire\Component;
use JaneCodeLife\LivewireSecureProperties\Unlocked;

new class extends Component {
// ✅ Secured: Locked by default, any client-side update will throw a Security Violation exception
public string $role = 'admin';

// 🔓 UNLOCKED: Updatable from client side via wire:model or client-side requests
#[Unlocked]
public string $name = 'Jane Joe';
};
?>



Name: {{ $name }}




Role: {{ $role }}


2. Multiple File Components (Class-based)

use Livewire\Component;
use JaneCodeLife\LivewireSecureProperties\Unlocked;

class UserProfile extends Component
{
// ✅ Secured: Locked by default, any client-side update will throw a Security Violation exception
public string $role = 'admin';

// 🔓 UNLOCKED: Updatable from client side via wire:model or client-side requests
#[Unlocked]
public string $name = 'Jane Joe';

public function render()
{
return view('livewire.user-profile');
}
}

Configuration

If you need to disable the package globally during specific environments (e.g., local debugging), you can add this environment variable to your .env file:

LIVEWIRE_SECURE_PROPERTIES_ENABLED=false

💖 Support

☕☕☕☕ Support me by coffee via USDT ☕☕☕☕

  • Network: TRX Tron (TRC20)
  • Address: TAFFjBP39Z86weL5dDU1A2251VrgPprDUj

Upcoming 🚀 (Stay Tuned!)

The Ultimate Neovim Config for Modern Web & Laravel Devs ⚡

I am currently cooking a comprehensive guide and boilerplate configuration on How to turn Neovim into a (Powerful) IDE explicitly optimized for:

  • Backend & Frameworks: PHP (Intelephense) & Full Laravel & Livewire Integration (With Preformance)
  • Frontend & Tooling: HTML, CSS, JavaScript, TypeScript, and Livewire SFCs
  • Speed: Blazing fast autocompletion, lightning-speed code navigation, and fuzzy finding.

Sumber Rujukan:

Comments

Introducing Fylgja UI

Fylgja CSS has always focused on the layer below components. Good defaults, design tokens,
and utilities that let you build whatever you need. But one question kept coming back. How
do you actually build a modal, a carousel, or a pagination bar with it?

Fylgja UI is the answer. It is a growing collection of components you can read,
copy, and paste into any project.

What it is

This is not another React UI library. There is no package to install, no component API to
learn, and no build step to adopt. What you get is markup, which means it works in Astro,
Laravel, Svelte, Rails, a single HTML file, and yes, in React too if that is where you
happen to be.

All of it is free. Where a lot of component collections keep their better half behind a
license, a subscription, or a pro tier, there is no paid version of Fylgja UI, and none is
planned. No account, no license key, no blocks that turn out to be locked once you need
them.

Every component is plain HTML. No wrapper divs to hold a layout together and no utility
class soup. Where the platform already gives you an element, the component uses it. Modals
and offcanvas panels are a native <dialog> opened with invoker commands, the accordion is
<details>, the progress bar is <progress>, and the rating input is a group of radio
buttons. That means focus handling, keyboard support, and assistive technology semantics
come from the browser instead of from code we would have to maintain.

The markup is classless wherever Fylgja already styles the bare element, so what you copy
stays close to what you would have written by hand. Each component page lists which Fylgja
packages it needs and which ones simply make it better, so you know exactly what you are
signing up for. A handful of components also ship a vanilla JavaScript or Alpine.js version
for the cases where state is genuinely needed.

The first batch covers thirty components across actions, forms, navigation, overlays,
feedback, data display, layout, and sliders.

This is only the beginning

That is a start, not a finished library. The gaps are obvious to anyone
who goes looking: tabs, tooltips, popovers, date pickers, steppers, and plenty more. Those
are coming, along with more variants and more framework versions of the components that are
already there.

CLI support is on the list too. Copy and paste works, but pulling a component into your
project should be as quick as npx fylgja@latest add already is for the
CSS files themselves. Same idea, one command, no dependency added
to your project.

What lands next depends a lot on what people actually need. If a component you were looking
for is missing, or an existing one does not cover your case,
open an issue. Ideas, requests, and
“this markup could be simpler” notes are all welcome. That feedback is the most reliable way
to keep the collection pointed at real problems instead of hypothetical ones.

A thank you to Anthropic

Fylgja UI exists in this form because Anthropic gave the project six months of Claude Max
for free, through the program they run for open source projects. Fylgja is a passion project
maintained in evenings and weekends, and writing thirty components with documentation,
examples, and previews is a lot of hours. Having Claude work through the drafts, the
repetitive parts, and the review passes turned something that would have taken months into
something that shipped. Thanks to Anthropic for making that possible.

The direction, the markup decisions, and the standards each component is held to are still
ours. The help was in getting there faster.

Go have a look at Fylgja UI, copy what you need, and tell us what is missing.


Sumber Rujukan:

  • Artikel asal dari Dev.to CSS
  • Published on haqis.com
Comments

After Rippling blew millions on AI in months, it built an employee ROI tool

HR software provider Rippling this week unveiled AI Spend Console, an anti-tokenmaxxing product that helps a company track and contain its AI spending. One of the most interesting features is that it maps how much individual employees, teams, and roles are spending and if they are genuinely more productive, or generally producing more AI slop.

The company promises the tool will show “which engineers have high AI spend whose peers frequently ask them to redo work in code reviews,” the company says in its blog post.

The tool was born after Rippling went all in on tokenmaxxing at the start of the year — as so many did — only to discover employees were wildly burning cash. Chief Product Officer Matt MacInnis still recalls the executive team meeting in March when CFO Adam Swiecicki presented a number that shocked them.

Rippling was on track to burn 40% of its R&D headcount budget on AI tokens, meaning it was spending as much on tokens as 40% of all the compensation it paid employees in that unit. Millions of dollars. (The R&D org is home to engineering at most tech companies.)

Spending was growing by 80% month-over-month, and if that trend continued, the next year it would spend almost as much on AI tokens — 90% — as it spent on its high-paid R&D unit employees.

“We were incredulous,” MacInnis told TechCrunch.

Management immediately undertook an “urgent” project to understand the spending and what they were getting for that money, he said. In fact, the launch ad for this new product features Swiecicki sitting on a stool while employees are picking up wads of cash and dumping them into a paper shredder.

When Rippling conducted an analysis, it discovered facts like “roughly 10–15% of our employees were driving about 60% of total AI spend. One engineer was spending $50,000 a month,” its blog post shared.

Rippling didn’t want to stop AI usage, just rein it in — a lot. It started by negotiating a max spending cap with each of the tools its company used: Cursor, OpenAI, and Anthropic. It immediately found an obvious issue: Employees defaulted to using the most recent, and most expensive, frontier models for all tasks.

“The truth is that the inference providers, like Anthropic and OpenAI, have absolutely no incentives to help you control your spend. They have every incentive for it to be a runaway expense, and that’s exactly what they do. They don’t provide you with great usage insight, and they don’t collaborate with one another,” MacInnis said.

That was a common early-2026 problem. Now, eight months into the year, enterprises have figured out a couple of things. First, they know they need multiple models from multiple AI labs at various price points, including a frontier open weight option, perhaps of Chinese origin.

Rippling founder and CEO Parker Conrad noted last month that when his company conducted its own benchmarks for its own internal uses, it discovered SpaceX’s Grok was the all-around leader but that “GLM 5.2 is 85% cheaper but [had] nearly identical performance” to the frontier models. (SpaceX now owns Cursor, which offers access to Grok and dozens of other models.) Z.ai’s GLM 5.2 has become a particular favorite Chinese model for coding tasks among tech companies these days. Databricks has also been championing it.

Second, enterprises now know they need an AI gateway that routes prompts to the best, most cost-effective model for the task. Rippling came to that conclusion too. So it built its own AI gateway that is also part of this product. MacInnis says it is possible for enterprises that already use another gateway to still use the AI Spend Console product, though if they want the features that govern spending, they would need to use Rippling’s gateway.

AI Spend Console produces dashboards (once known as leaderboards in the tokenmaxxing days) that score attributes such as prompts per day combined with work output (lines of code/pull requests) and spend.

With this tool in place, Rippling said it dropped its token spend from 40% of its headcount budget to about 15%. But it didn’t curtail AI usage. The company spent a peak of 605 billion tokens the month the CFO issued his warning, MacInnis shared. In July, internal usage hit 600 billion tokens again, yet “the cost of July’s token spend was 37% of the cost of April’s token spend,” he said.

“That’s just because now we’re routing to the more effective models,” he said, joking that “we’re not letting the sales team do grammar updates using Fable.”

But technology solutions aren’t enough, Rippling notes. The company found people using AI effectively and made them “AI captains” tasked with assisting the rest of the company.

Still, such efforts to use AI beyond engineering are a work in progress, MacInnis says, as software engineers have been the primary users so far. But Rippling is, for example, working on it for customer onboarding teams to automate some mailing data and data-reconciliation tasks. The dashboard will then measure productivity in terms of onboarding more customers.

“We have to be able to link token consumption in G&A functions and in customer-facing functions back to productivity. If we can’t do that, all bets are off on any of this stuff being available to the broader employee base,” MacInnis says.

So, if Rippling is an example, tokenmaxxing may have swung so far the other direction that employee AI access may no longer be like Slack or email. If the company can’t measure productivity, then all employees might not have access.

As for the product, AI Spend Console is included for Rippling’s HR subscribers, though there are additional AI usage-based costs. It can also be purchased as a stand-alone product and integrated with another HR system of record, MacInnis says.


Sumber Rujukan:

Comments

Add Livewire modals in Laravel with Wiremodal

Wiremodal is a framework-agnostic modal package for Laravel, which allows to handle modals, so you don’t have co configure them in all your projects.

It ships a few Livewire-side helpers that make exactly this pleasant. This post is the Livewire integration end to end: opening and closing from PHP, delivering a payload on open, the one trap to avoid, and the optional form panel for when a modal happens to be a form.

How to install

Pull the package in and get the assets onto the page.

composer require edulazaro/wiremodal
php artisan vendor:publish --tag=wiremodal-assets

The service provider auto-registers and there is no config file. Point your layout at the published files:


If you bundle with Vite, skip the publish and import straight from the vendor directory instead, so a package update flows through without re-publishing anything:

/* resources/css/app.css */
@import "../../vendor/edulazaro/wiremodal/resources/css/wiremodal.css";

// resources/js/app.js
import '../../vendor/edulazaro/wiremodal/resources/js/wiremodal.js';

Opening and closing from Livewire

Define the modal once with the <x-wiremodal> component, give it a name, and fill the body and footer slots. Here is a delete confirmation:



This action cannot be undone.







The Cancel button carries data-wm-dismiss, and any element with that attribute closes the modal it sits in, so you never write a cancel handler. To open and close from the component itself, use the macros the package registers on every Livewire component:

public function confirmDelete(): void
{
$this->openModal('confirm-delete');
}

public function destroy(): void
{
// delete the record...
$this->closeModal('confirm-delete');
}

That is the whole open and close cycle from the server, and there is nothing to import: openModal and closeModal are macros wiremodal adds for you.

Handing the modal a payload

A confirmation needs no data, but most modals do: the row you clicked. openModal() takes a second argument, and Wiremodal delivers it to the modal through a wiremodal:opened event whose detail is { name, data }, fired on both the modal element and window. You read it with a one-line Alpine handler.

public function view(int $id): void
{
$task = Task::findOrFail($id);

$this->openModal('task-detail', [
'id' => $task->id,
'title' => $task->title,
]);
}

    x-data="{ task: {} }"
@wiremodal:opened.window="if ($event.detail.name === 'task-detail') task = $event.detail.data || {}">






The if ($event.detail.name === ...) guard matters because the event fires on window for every modal, so each one only reacts to its own payload. The panel fills with the right row the instant it opens.

The positional-dispatch trap

There is one mistake worth calling out, because it fails silently. Do not skip the macro and dispatch the browser event yourself with a positional string:

// Silently never opens the modal
$this->dispatch('open-wiremodal', 'task-detail');

Livewire wraps that positional string into an array, so the browser receives e.detail = ['task-detail']. Wiremodal’s parser accepts a bare string or an object with a name key, never an array, so the modal just never opens and no error is thrown. The openModal() and closeModal() macros exist precisely to avoid this, since they dispatch with named arguments under the hood. If you ever dispatch by hand, use the named form:

$this->dispatch('open-wiremodal', name: 'task-detail', data: ['id' => 42]);

When the modal is a form

Everything above works for any modal. When the modal happens to be a form and you want native submission, the Enter key and a real submit button, add as="form". It renders the panel as a <form> instead of a <div>, so wire:submit fires on submit and the autofocus attribute is honored.

    as="form" wire:submit="save"
x-data
@wiremodal:opened.window="
if ($event.detail.name === 'edit-task') {
$wire.set('editingId', $event.detail.data.id);
$wire.set('title', $event.detail.data.title);
}
">







public ?int $editingId = null;
public string $title = '';

public function save(): void
{
Task::findOrFail($this->editingId)->update(['title' => $this->title]);
$this->closeModal('edit-task');
}

You open it exactly as before with openModal('edit-task', [...]); the only difference is as="form" turning the panel into a real form so Enter and the submit button drive wire:submit. Without it you would hang a wire:click on the Save button instead. That is the point: the form is one prop, not the price of entry.

Sizes and persistent modals

Two props round it out. size takes one of eleven named widths and defaults to 2xl (42rem), running from xs (20rem) to 7xl (80rem); a value outside the catalog throws an InvalidArgumentException at render, so a typo fails loudly instead of producing a wrong-sized box.

Use fullscreen to fill the viewport. And persistent makes a modal ignore overlay clicks and the ESC key, so a wizard step or a you must choose prompt only closes through an explicit data-wm-dismiss button or a programmatic closeModal().


👉 Package on Packagist: https://packagist.org/packages/edulazaro/wiremodal
👉 Source on GitHub: https://github.com/edulazaro/wiremodal


Sumber Rujukan:

Comments

traceless-style Atomic CSS That Leaves No Trace


If you’re still shipping a CSS-in-JS runtime just to color a button, it’s worth asking why.

traceless-style compiles every style at build time — zero runtime, zero CSS-in-JS engine on the page. What you ship is clean, deduped atomic CSS.

A quick look

import { tl } from "traceless-style";

const $ = tl.create({
card: {
padding: "1.5rem",
background: "#ffffff",
color: "#0f172a",
borderRadius: "12px",

_hover: { transform: "translateY(-2px)" },
_dark: { background: "#13131a" },
},
});

...
;

This compiles down to plain atomic classes:

.tlm92pvu { padding: 1.5rem }
.tla7dffa { background: #ffffff }
.tlb1c4lk { color: #0f172a }
.tlc883bz { border-radius: 12px }
.tld5e2f1:hover { transform: translateY(-2px) }
.dark .tla7dffa { background: #13131a }

No styled-components wrapper. No Babel plugin. No CSS-in-JS engine running in the browser.

What makes it different

Zero runtime, by construction

Every tl.create call is statically transformed at build time into a class-string literal. The only runtime helper left is ~2 KB, and it exists solely for SSR and non-bundled tests.

WCAG built into the build

This is the part that stands out. Every style block is checked against WCAG 2.1 §1.4.3 (AA 4.5:1), §1.4.6 (AAA 7:1), §1.4.11 (UI 3:1), and §2.4.13 (focus 3:1) — before your CSS is written to disk.


Sumber Rujukan:

  • Artikel asal dari Dev.to CSS
  • Published on haqis.com
Comments

Half My Tests Failed and None Were Broken: My Shell Poisoned PHPUnit

For about a day I believed I had roughly 150 broken tests. I was wrong — not one of them was broken. Then I was wrong about the cause. Twice.

Here’s the walk from “our test suite is rotting” to “one stray line in my shell was winning a fight I didn’t know it was in.”

The symptom

I ran the suite and about half of it was red. Every failure was the same shape: 403 org-access-denied. Tenant-scoped tests, denied across the board.

It looked exactly like accumulated test debt — the kind of thing you file, sigh at, and schedule for “later.” So that’s what I did. I opened an issue: ~150 failing tests, org access denied.

Correction #1: the tests were fine, my machine wasn’t

Before scheduling the cleanup, I ran the suite in a clean environment — a fresh shell with none of my profile loaded.

All green.

Not “fewer failures.” Zero. The tests were fine. The failures were a property of my environment, not the code. Which is a worse feeling than a broken test, honestly, because it means the call is coming from inside the house.

The mechanism turned out to be a PHPUnit detail I’d never had to think about: env vars you set in the config do not override an env var that’s already set in your shell — not unless you mark them force="true". My shell profile exported a variable that the test config also set, and by default, the shell won. So my tests were quietly running against the wrong context, and everything tenant-scoped got denied.

Correction #2: I blamed the scary variable, not the guilty one

Here’s the part I’m least proud of and find most useful.

My first theory for which variable was a JWT signing secret. It’s the dangerous-sounding one; of course a bad secret breaks auth. I wrote it up that way.

It was wrong, and the code says why. A signing secret is symmetric: if signing and verifying both read the same (wrong) value, the tokens are still internally valid. A polluted secret doesn’t produce 403 org-access-denied — it produces valid tokens for a wrong-but-consistent world. It couldn’t be the cause.

The actual culprit was boring: a tenant-slug variable. My shell had it set to one thing; the tests minted tokens for another. So the request would resolve one tenant from the env, carry a token for a different tenant, and the access check — correctly — denied it. Every tenant-scoped test, 403.

I’d spent my first guess on the variable that sounded like a security problem, when a plain identifier was doing all the damage.

The fix was one attribute

force="true" on the env entries, so the test configuration wins over whatever the shell happens to export. That’s it. The suite is now hermetic: it runs the same on my machine, on a colleague’s, and in CI, regardless of anyone’s shell profile.

I proved it the honest way — by injecting the bad variable on purpose. With the injection and no force, the suite collapses (about 150 failures and a pile of errors). With force, the same injection does nothing; everything stays green. The fix is verified against the attack, not against my memory.

What I can’t tell you

I can’t tell you the exact line that was in my shell profile that day. I don’t have it pinned down, and I’m not going to invent it to make the story cleaner. What I have is a reproduction — inject the variable, watch it break — and that’s what the fix is proven against.

The count is fuzzy too. It was around 150 failures out of somewhere near 290 tests, and both numbers drifted day to day as the suite changed. “156 out of 291” would look precise and be a small lie. Half the suite red, zero tests actually broken is the true and useful shape of it.

Two takeaways

Make your tests hermetic. Test configuration should beat the machine it runs on, unconditionally — force, or the equivalent in your stack. A suite whose result depends on the developer’s shell isn’t testing your code; it’s testing your dotfiles.

When you’re debugging, suspect the boring variable. I lost real time pointing at a secret because it sounded dangerous, while a plain tenant id sat there quietly denying everything. The scary-looking cause is a great way to feel productive and stay wrong.

What’s the worst “it’s the tests” you’ve had that turned out to be your own environment?

── Hideyuki Mori (Ayane International) 🔗 hideyuki-mori.com


Sumber Rujukan:

  • Artikel asal dari Dev.to PHP
  • Published on haqis.com
Comments