meteor-security
Skill10 starsChanged 3 months agoMITView on GitHub
Use when auditing or hardening a Meteor 3 application. Triggers on missing check() on method arguments, missing this.userId guards on publications, browser-policy CSP, DDPRateLimiter rules, oauth-encryption via Accounts.config oauthSecretKey, audit-argument-checks, allow/deny legacy patterns, BrowserPolicy.content.disallowInlineScripts, BrowserPolicy.framing.disallow. Use this skill when the user asks about hardening, asks about a security review, or asks about CSP for a third-party script (Stripe, Google Maps, fonts).
1 other project uses a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- wekan/wekanedited, first seen Oct 8, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- wekan/wekan★ 21k95% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository21 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/CLAUDE.mdCLAUDE.md
- release-publishingSkill
- skill-gap-auditSkill
- skill-maintenanceSkill
- meteor-accountsSkill
- meteor-blazeSkill
- meteor-cli-installationSkill
- meteor-community-packagesSkill
- meteor-debuggingSkill
- meteor-deploymentSkill
- meteor-methodsSkill
- meteor-modern-build-stackSkill
- meteor-mongo-minimongoSkill
- meteor-nativeSkill
- meteor-pubsubSkill
- meteor-reactSkill
- meteor-testingSkill
- meteor-typescriptSkill
- migrate-to-meteor-3Skill
- migrate-to-rspackSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 1 is distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 35
Add audit-argument-checks in dev to catch missing check().
- Very specific: line 36
Add browser-policy and configure CSP.
- Very specific: line 37
Add DDPRateLimiter rules for sensitive methods (login, password reset, resource creation).
- Very specific: line 39
If the app uses OAuth, set oauthSecretKey to encrypt provider secrets at rest.
- Very specific: line 51
Keep Meteor.users.deny({ update: () => true }) on the selected collection when all client profile writes must be blocked, until an …
- Very specific: line 143
Only matcher fields contribute to the rate-limit bucket key.
- Very specific: line 167
Add oauth-encryption and pass a 16-byte base64 key (NOT 32 bytes) to Accounts.config at module top level (not inside Meteor.startup):
- Very specific: line 217
Use BrowserPolicy.content.allowOriginForAll for a third-party script.
- Very specific: line 222
Call Accounts.config({ oauthSecretKey }) inside Meteor.startup.
- Distinctive: line 223
Must be at module top level so it loads before the OAuth packages read it.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Meteor security
Meteor's security model is opinionated: the server holds authority, the client cannot be trusted, and the only places that filter data before it reaches users are methods (write paths) and publications (read paths).
Decision flow
- Audit every method: does it
check()every argument and guard onthis.userIdorMeteor.userId()when authentication matters? - Audit every publication: does it filter by
this.userId(when user-specific) and project columns withfields? - Add
audit-argument-checksin dev to catch missingcheck(). - Add
browser-policyand configure CSP. - Add
DDPRateLimiterrules for sensitive methods (login, password reset, resource creation). - If the app uses OAuth, set
oauthSecretKeyto encrypt provider secrets at rest. - Replace permissive client-write rules with authenticated methods. Keep
protective
denyrules until an equivalent method-only policy is active; Accounts can add an owner-onlyprofileallowance even without an appallowrule. See the profile policy below.
Accounts collection and profile policy
In Meteor 3.6's fixed Accounts pairing, custom users collections receive account
indexes and, when client mutation methods exist, the default owner-only profile
allowance. Configure the collection on both runtimes before installing guards.
Keep Meteor.users.deny({ update: () => true }) on the selected collection when
all client profile writes must be blocked, until an equivalent policy is proven.
defineMutationMethods: false supplies a method-only collection without those
validators; the RC still creates account indexes. Earlier packages require
checking their setup behavior. See meteor-accounts for collection identity,
secondary Accounts instances and package boundaries. Never trust roles stored
in a client-editable profile.
Method guard checklist
import { Meteor } from "meteor/meteor";
import { check, Match } from "meteor/check";
Meteor.methods({
async updateProfile(payload) {
check(payload, { displayName: String, bio: Match.Optional(String) });
if (!this.userId) {
throw new Meteor.Error("not-authorized");
}
await Meteor.users.updateAsync(this.userId, { $set: { profile: payload } });
},
async updateAddress(payload) {
check(payload, String);
if (!Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
await Meteor.users.updateAsync(Meteor.userId(), { $set: { address: payload } });
},
});Reject any method that does not match: check on every argument, userId
gate when needed, Meteor.Error(code, reason) for failures, *Async
Mongo on the server.
Publication guard checklist
Meteor.publish("items.mine", function () {
if (!this.userId) return this.ready();
return Items.find(
{ ownerId: this.userId },
{ fields: { title: 1, qty: 1 }, limit: 200 },
);
});Reject any publication that returns an unbounded cursor, omits the field projection, or skips a userId filter on user-specific data.
CSP via browser-policy
meteor add browser-policy// server top-level or inside Meteor.startup
import { BrowserPolicy } from "meteor/browser-policy-common";
import { Meteor } from "meteor/meteor";
Meteor.startup(async () => {
await BrowserPolicy.content.disallowInlineScripts();
BrowserPolicy.content.disallowEval();
BrowserPolicy.framing.disallow();
});BrowserPolicy is server-only. Configure it during module initialization or
startup so every request receives one deterministic process-wide policy. The
current implementation invalidates its cached CSP after a mutation, but do not
mutate this global policy per request or per user. See
references/browser-policy-csp.md for recipes (Stripe, Google Maps,
fonts, inline-style allowance).
DDPRateLimiter for sensitive methods
import { DDPRateLimiter } from "meteor/ddp-rate-limiter";
DDPRateLimiter.addRule(
{
type: "method",
name: "login",
clientAddress: () => true,
},
5,
60000, // 5 attempts per 60s, per IP
);Only matcher fields contribute to the rate-limit bucket key. Without
clientAddress, connectionId, or userId, every matching caller shares one
global bucket. Meteor 3.5+ permits async matcher functions for database-backed
decisions; keep their queries fast because the connection waits for them. On
Meteor 3.0 through 3.4, matchers must stay synchronous. Use a fixed rule,
precomputed synchronous state, or upgrade rather than awaiting Mongo there.
The default rule (5 in 10s per matched method per DDP connection for login / signup / password
reset) ships with accounts-base. Meteor 3.6-beta.1's
accounts-base@3.4.0-beta360.1 also matches requestLoginTokenForUser;
accounts-passwordless@3.1.2-beta360.1 validates its complete payload before
lookup/creation. The RC pairing is accounts-base@3.4.0-rc360.0 with
accounts-passwordless@3.1.2-rc360.0; handle callback rate errors without
an immediate retry loop. Inspect both package versions on older apps. Remove the
default rule with Accounts.removeDefaultRateLimit() only if you replace it.
For cookie hardening on accounts-base@3.4.0-beta360.1, use meteor-accounts:
trusted origins, valid tokens, no-store responses and SameSite=Strict have
specific migration checks. Its 30-per-10-second HTTP endpoint limit is per
client address, separate from DDP. HttpOnly storage does not stop active
same-origin XSS from obtaining the resume token used for DDP authentication.
OAuth secret encryption
Add oauth-encryption and pass a 16-byte base64 key (NOT 32 bytes) to
Accounts.config at module top level (not inside Meteor.startup):
meteor node -e "console.log(require('crypto').randomBytes(16).toString('base64'))"import { Accounts } from "meteor/accounts-base";
Accounts.config({
oauthSecretKey: Meteor.settings.oauthSecretKey,
});At startup, accounts-oauth seals an unsealed provider application secret at
ServiceConfiguration.configurations.secret. Provider packages also seal
supported per-user token fields, such as services.github.accessToken or
Twitter's accessTokenSecret. There is no generic
Meteor.users.services.<provider>.secret field. Inspect the provider schema
before asserting which user credential is encrypted.
audit-argument-checks
meteor add audit-argument-checksThrows if any method or publication runs without check() covering
every argument. Methods that legitimately accept arbitrary input declare
this explicitly:
Meteor.methods({
rawLog(...args) {
check(args, [Match.Any]);
// ...
},
});Anti-patterns
- Permissive client-write rules for sensitive data. Replace with guarded methods; do not remove protective denial before proving equivalent policy.
Meteor.settings.public.<secret>. The client seespublic. Move secrets to the top level ofsettings.json.- Publish the entire
Meteor.userscollection. Always project (e.g.fields: { username: 1, profile: 1 }) and filter. Publish email only to the owning user or another explicitly authorized audience. - Use
BrowserPolicy.content.allowOriginForAllfor a third-party script. It grants the origin to every current content directive. Allow only the script, frame, connect, image, style, or font directives the integration needs. - Methods that accept callback-shaped arguments. Functions cannot travel over DDP.
- Call
Accounts.config({ oauthSecretKey })insideMeteor.startup. Must be at module top level so it loads before the OAuth packages read it.
See also
references/method-and-publish-guards.mdreferences/browser-policy-csp.mdreferences/eval-cases.md- Related skills:
meteor-methods,meteor-pubsub,meteor-accounts.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

