Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/src/content/docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ title: Rules reference
description: The defense rules shipped with agent-browser-shield, what each one does, and its default state.
---

The extension ships 35 rules, each independently toggleable from the extension
popup. Rules marked **default: on** are active on fresh install; **default:
off** rules must be enabled manually.
The extension ships 35 rules, each independently toggleable from the extension's
options page. Rules marked **default: on** are active on fresh install;
**default: off** rules must be enabled manually.

Rules marked **top frame only** never run inside iframes — useful for page-wide
targets (footers, cookie overlays, URL recipes) so they don't fire pointlessly
in every embedded frame.

This page groups rules by the threat or pattern they defend against. The popup
itself shows a flat list; rule IDs here match the filenames in
This page groups rules by the threat or pattern they defend against, the same
grouping the options page uses; rule IDs here match the filenames in
[`extension/src/rules/`](https://github.com/pixiebrix/agent-browser-shield/tree/main/extension/src/rules),
which is the authoritative source for behavior. Initial enabled/disabled state
for each rule lives in
Expand Down Expand Up @@ -847,7 +847,7 @@ reaches — applied to SVG `<symbol>` definitions at runtime.
- **Scope:** top frame only
- **Availability:** requires an OpenAI API key — either bundled at build time
via `OPENAI_API_KEY`, or saved on the extension's options page. Until a key is
configured the rule shows as Unavailable in the popup and options.
configured the rule shows as Unavailable on the options page.

Use a small LLM to identify engagement/exploration rails (related products, "you
might also like", recommended articles, trending now, etc.) and replace them
Expand Down
24 changes: 5 additions & 19 deletions extension/src/lib/RuleList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,21 @@ function ruleById(id: RuleId): Rule {
return rule;
}

// Shared per-rule checkbox list rendered by both the popup and options page.
// Rules are grouped by the same top-level threat/pattern categories used on
// the docs Rules reference page; the catalog test enforces every rule belongs
// to exactly one group.
//
// Pass `disabledByEnforcement` when the surface should grey out every rule
// (popup, when global enforcement is off); the options page omits it because
// the user can still edit preferences while enforcement is paused.
// Per-rule checkbox list rendered on the options page. Rules are grouped by
// the same top-level threat/pattern categories used on the docs Rules reference
// page; the catalog test enforces every rule belongs to exactly one group.
export function RuleList({
states,
availability,
disabledByEnforcement = false,
className,
}: {
states: RuleStates;
availability: RuleAvailabilityStates;
disabledByEnforcement?: boolean;
className?: string;
}) {
const rootClass = className ? `rule-groups ${className}` : "rule-groups";
return (
<div
className={
disabledByEnforcement
? `${rootClass} rule-groups--enforcement-off`
: rootClass
}
>
<div className={rootClass}>
{RULE_GROUPS.map((group) => (
<section key={group.id} className="rule-group">
<h3 className="rule-group__heading">{group.label}</h3>
Expand All @@ -57,7 +44,6 @@ export function RuleList({
const rule = ruleById(ruleId);
const snapshot = availability[rule.id];
const unavailable = !snapshot?.available;
const disabled = unavailable || disabledByEnforcement;
return (
<li
key={rule.id}
Expand All @@ -67,7 +53,7 @@ export function RuleList({
<input
type="checkbox"
checked={unavailable ? false : states[rule.id]}
disabled={disabled}
disabled={unavailable}
onChange={(event) => {
void setRuleEnabled(rule.id, event.target.checked);
}}
Expand Down
79 changes: 0 additions & 79 deletions extension/src/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -170,85 +170,6 @@
font-size: 10px;
color: #888;
}
.rule-groups {
display: flex;
flex-direction: column;
gap: 14px;
}
.rule-groups--enforcement-off {
opacity: 0.55;
}
.rule-group__heading {
margin: 0 0 6px;
padding-bottom: 4px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #555;
border-bottom: 1px solid #e4e4e7;
}
.rules {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.rules label {
display: flex;
gap: 8px;
align-items: flex-start;
cursor: pointer;
padding: 6px;
border-radius: 4px;
}
.rules label:hover {
background: #f4f4f5;
}
.rules input {
margin-top: 2px;
}
.rules strong {
display: block;
font-weight: 600;
}
.rules p {
margin: 2px 0 0;
font-size: 11px;
color: #666;
line-height: 1.3;
}
.rules .rule--unavailable label {
cursor: not-allowed;
}
.rules .rule--unavailable strong,
.rules .rule--unavailable p {
color: #999;
}
.rules .rule--unavailable input {
cursor: not-allowed;
}
.rules .unavailable-reason {
margin: 2px 0 0;
font-style: italic;
color: #92400e;
}
.badge {
display: inline-block;
margin-left: 6px;
padding: 1px 6px;
font-size: 10px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #92400e;
background: #fef3c7;
border: 1px solid #fde68a;
border-radius: 3px;
vertical-align: middle;
}
.options-button-toggle {
display: flex;
align-items: center;
Expand Down
19 changes: 2 additions & 17 deletions extension/src/popup/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
// Copyright (c) 2026 PixieBrix, Inc.
// Licensed under PolyForm Shield 1.0.0 — see LICENSE.

import { availabilitySource } from "../lib/availability";
import { enforcementStorage } from "../lib/enforcement";
import { HelpLinks } from "../lib/HelpLinks";
import { optionsButtonStorage } from "../lib/options-button-toggle";
import { RuleList } from "../lib/RuleList";
import { ruleStatesStorage } from "../lib/storage";
import { useChromeStorageValue } from "../lib/use-chrome-storage-value";
import { DetectionsSection } from "./DetectionsSection";

export function Popup() {
const states = useChromeStorageValue(ruleStatesStorage);
const enforcementEnabled = useChromeStorageValue(enforcementStorage);
const availability = useChromeStorageValue(availabilitySource);
const optionsButtonEnabled = useChromeStorageValue(optionsButtonStorage);

if (
!states ||
enforcementEnabled === null ||
!availability ||
optionsButtonEnabled === null
) {
if (enforcementEnabled === null || optionsButtonEnabled === null) {
return <div className="loading">Loading…</div>;
}

Expand Down Expand Up @@ -56,17 +46,12 @@ export function Popup() {
</label>
{!enforcementEnabled && (
<p className="enforcement__hint">
All rules are paused for every tab. Your per-rule selection below is
All rules are paused for every tab. Your per-rule selection is
preserved and restored when you turn enforcement back on.
</p>
)}
</div>
<DetectionsSection />
<RuleList
states={states}
availability={availability}
disabledByEnforcement={!enforcementEnabled}
/>
<label className="options-button-toggle">
<span className="options-button-toggle__text">
<strong>On-page options button</strong>
Expand Down
Loading