
Research
/Security News
npm Package Uses Prompt Injection and Token Flooding to Disrupt AI Malware Scanners
A new npm package tests AI malware scanners with prompt injection, safety-triggering comments, context flooding, and obfuscated JavaScript.
@silverassist/recaptcha
Advanced tools
Google reCAPTCHA v3 integration for Next.js applications with Server Actions support
Google reCAPTCHA v3 integration for Next.js applications with Server Actions support.
RecaptchaWrapper for automatic token generationvalidateRecaptcha function for Server Actionsnpm install @silverassist/recaptcha
# or
yarn add @silverassist/recaptcha
# or
pnpm add @silverassist/recaptcha
# .env.local
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=your_site_key_here
RECAPTCHA_SECRET_KEY=your_secret_key_here
Add RecaptchaWrapper inside your form:
"use client";
import { RecaptchaWrapper } from "@silverassist/recaptcha";
export function ContactForm() {
return (
<form action={submitForm}>
<RecaptchaWrapper action="contact_form" />
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}
Validate the token in your Server Action:
"use server";
import { validateRecaptcha, getRecaptchaToken } from "@silverassist/recaptcha/server";
export async function submitForm(formData: FormData) {
// Get and validate reCAPTCHA token
const token = getRecaptchaToken(formData);
const recaptcha = await validateRecaptcha(token, "contact_form");
if (!recaptcha.success) {
return { success: false, message: recaptcha.error };
}
// Process form data...
const email = formData.get("email");
const message = formData.get("message");
// Your form processing logic here
return { success: true };
}
RecaptchaWrapper injects a hidden input field containing the reCAPTCHA token. If your form handler creates a custom FormData object, you must ensure the hidden token is included.
"use client";
import { RecaptchaWrapper } from "@silverassist/recaptcha";
import { submitForm } from "./actions"; // Your server action
export function ContactForm() {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// ❌ Creating empty FormData - hidden reCAPTCHA input is NOT included!
const formData = new FormData();
formData.set("email", "user@example.com");
formData.set("message", "Hello");
await submitForm(formData);
};
return (
<form onSubmit={handleSubmit}>
<RecaptchaWrapper action="contact_form" />
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}
"use client";
import { RecaptchaWrapper } from "@silverassist/recaptcha";
import { submitForm } from "./actions"; // Your server action
export function ContactForm() {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// ✅ Pass form element - captures ALL inputs including hidden reCAPTCHA token
const formData = new FormData(e.currentTarget);
await submitForm(formData);
};
return (
<form onSubmit={handleSubmit}>
<RecaptchaWrapper action="contact_form" />
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}
"use client";
import { RecaptchaWrapper } from "@silverassist/recaptcha";
import { submitForm } from "./actions"; // Your server action
export function ContactForm() {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// ✅ Start with form element (includes hidden token)
const formData = new FormData(e.currentTarget);
// Then add/override specific fields
formData.set("customField", "customValue");
formData.set("timestamp", Date.now().toString());
await submitForm(formData);
};
return (
<form onSubmit={handleSubmit}>
<RecaptchaWrapper action="contact_form" />
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}
The lazy prop enables lazy loading of the reCAPTCHA script, which defers loading until the form becomes visible in the viewport. This significantly improves initial page load performance.
Note: These metrics are approximate values measured on production websites using Google reCAPTCHA v3. Actual performance improvements will vary based on network conditions, device capabilities, and page complexity.
| Metric | Without Lazy Loading | With Lazy Loading | Improvement |
|---|---|---|---|
| Initial JS | 320KB+ | 0 KB (until visible) | -320KB |
| TBT (Total Blocking Time) | ~470ms | ~0ms (deferred) | -470ms |
| TTI (Time to Interactive) | +2-3s | Minimal impact | -2-3s |
Enable lazy loading by adding the lazy prop:
"use client";
import { RecaptchaWrapper } from "@silverassist/recaptcha";
export function ContactForm() {
return (
<form action={submitForm}>
{/* Script loads only when form is near viewport */}
<RecaptchaWrapper action="contact_form" lazy />
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}
Control when the script loads with the lazyRootMargin prop (default: "200px"):
// Load script earlier (400px before entering viewport)
<RecaptchaWrapper action="contact_form" lazy lazyRootMargin="400px" />
// Load script later (load only when fully visible)
<RecaptchaWrapper action="contact_form" lazy lazyRootMargin="0px" />
// Load with negative margin (load only after scrolling past)
<RecaptchaWrapper action="contact_form" lazy lazyRootMargin="-100px" />
// Hero form (above the fold) - load immediately
<RecaptchaWrapper action="hero_signup" />
// Footer form (below the fold) - lazy load
<RecaptchaWrapper action="footer_contact" lazy />
The package automatically uses singleton script loading, so the script is only loaded once even with multiple forms:
export function MultiFormPage() {
return (
<>
{/* First form triggers script load */}
<RecaptchaWrapper action="newsletter" lazy />
{/* Second form reuses the same script */}
<RecaptchaWrapper action="contact" lazy />
{/* Third form also reuses the script */}
<RecaptchaWrapper action="feedback" lazy />
</>
);
}
// Form near top - smaller margin for faster load
<RecaptchaWrapper action="signup" lazy lazyRootMargin="100px" />
// Form far down page - larger margin to load in advance
<RecaptchaWrapper action="newsletter" lazy lazyRootMargin="500px" />
Client component that loads reCAPTCHA and generates tokens.
<RecaptchaWrapper
action="contact_form" // Required: action name for analytics
inputName="recaptchaToken" // Optional: hidden input name (default: "recaptchaToken")
inputId="recaptcha-token" // Optional: hidden input id
siteKey="..." // Optional: override env variable
refreshInterval={90000} // Optional: token refresh interval in ms (default: 90000)
onTokenGenerated={(token) => {}} // Optional: callback when token is generated
onError={(error) => {}} // Optional: callback on error
lazy={false} // Optional: enable lazy loading (default: false)
lazyRootMargin="200px" // Optional: IntersectionObserver rootMargin (default: "200px")
/>
| Prop | Type | Default | Description |
|---|---|---|---|
action | string | Required | Action name for reCAPTCHA analytics (e.g., "contact_form", "signup") |
inputName | string | "recaptchaToken" | Name attribute for the hidden input field |
inputId | string | "recaptcha-token" | ID attribute for the hidden input field |
siteKey | string | process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY | Override the site key from environment variable |
refreshInterval | number | 90000 | Token refresh interval in milliseconds (90 seconds) |
onTokenGenerated | (token: string) => void | undefined | Callback invoked when a new token is generated |
onError | (error: Error) => void | undefined | Callback invoked when an error occurs |
lazy | boolean | false | Enable lazy loading to defer script until form is visible |
lazyRootMargin | string | "200px" | IntersectionObserver rootMargin (used when lazy is true) |
Server-side token validation function.
const result = await validateRecaptcha(
token, // Token from form
"contact_form", // Expected action (optional)
{
scoreThreshold: 0.5, // Minimum score (default: 0.5)
secretKey: "...", // Override env variable
debug: true, // Enable debug logging
}
);
// Result type:
// {
// success: boolean,
// score: number,
// error?: string,
// skipped?: boolean,
// rawResponse?: RecaptchaVerifyResponse
// }
Check if reCAPTCHA is configured.
import { isRecaptchaEnabled } from "@silverassist/recaptcha/server";
if (isRecaptchaEnabled()) {
// Validate token
} else {
// Skip validation (development)
}
Extract token from FormData.
import { getRecaptchaToken } from "@silverassist/recaptcha/server";
const token = getRecaptchaToken(formData);
const token = getRecaptchaToken(formData, "customFieldName");
reCAPTCHA v3 returns a score from 0.0 to 1.0:
| Score | Meaning |
|---|---|
| 1.0 | Very likely human |
| 0.7+ | Likely human |
| 0.5 | Default threshold |
| 0.3- | Suspicious |
| 0.0 | Very likely bot |
Adjust threshold based on form sensitivity:
// Standard forms
await validateRecaptcha(token, "contact", { scoreThreshold: 0.5 });
// Sensitive forms (payments, account creation)
await validateRecaptcha(token, "payment", { scoreThreshold: 0.7 });
// Low-risk forms (newsletter signup)
await validateRecaptcha(token, "newsletter", { scoreThreshold: 0.3 });
You can import from specific subpaths for better tree-shaking:
// Main exports (client + server + types)
import { RecaptchaWrapper, validateRecaptcha } from "@silverassist/recaptcha";
// Client only
import { RecaptchaWrapper } from "@silverassist/recaptcha/client";
// Server only
import { validateRecaptcha, getRecaptchaToken, isRecaptchaEnabled } from "@silverassist/recaptcha/server";
// Types only
import type { RecaptchaValidationResult, RecaptchaWrapperProps } from "@silverassist/recaptcha/types";
// Constants only
import { DEFAULT_SCORE_THRESHOLD, RECAPTCHA_CONFIG } from "@silverassist/recaptcha/constants";
In development, when RECAPTCHA_SECRET_KEY is not set, validation is skipped and forms work normally. This allows testing without reCAPTCHA credentials.
const result = await validateRecaptcha(token, "test");
// Returns: { success: true, score: 1, skipped: true }
Full TypeScript support with exported types:
import type {
RecaptchaWrapperProps,
RecaptchaValidationResult,
RecaptchaVerifyResponse,
RecaptchaConfig,
RecaptchaValidationOptions,
} from "@silverassist/recaptcha";
Polyform Noncommercial License 1.0.0
FAQs
Google reCAPTCHA v3 integration for Next.js applications with Server Actions support
We found that @silverassist/recaptcha demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
A new npm package tests AI malware scanners with prompt injection, safety-triggering comments, context flooding, and obfuscated JavaScript.

Product
Socket now detects supply chain risks in project manifests, starting with missing lockfiles that can make dependency installs non-reproducible.

Research
/Security News
The trojanized extensions use TinyGo-compiled WebAssembly and Solana transaction memos to resolve command-and-control infrastructure.