@cosmstack/blackshield
Version:
A developer-first security toolkit for React/Next.js applications
199 lines (194 loc) • 6.11 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/rules/index.ts
var index_exports = {};
__export(index_exports, {
configs: () => configs,
default: () => index_default,
rules: () => rules
});
module.exports = __toCommonJS(index_exports);
// src/rules/no-public-sensitive-env.ts
var import_utils = require("@typescript-eslint/utils");
var createRule = import_utils.ESLintUtils.RuleCreator(
(name) => `https://github.com/cosmstack/blackshield/blob/main/docs/rules/${name}.md`
);
var SENSITIVE_PATTERNS = [
/SECRET/i,
/KEY/i,
/TOKEN/i,
/PASSWORD/i,
/PRIVATE/i,
/API_SECRET/i,
/DATABASE/i,
/DB_/i,
/MONGO/i,
/REDIS/i,
/JWT/i,
/AUTH/i,
/STRIPE_SECRET/i,
/PAYPAL/i,
/WEBHOOK/i,
/ENCRYPTION/i,
/HASH/i,
/SALT/i
];
var noPublicSensitiveEnv = createRule({
name: "no-public-sensitive-env",
meta: {
type: "problem",
docs: {
description: "Prevent access to sensitive environment variables exposed through NEXT_PUBLIC_"
},
fixable: "code",
schema: [
{
type: "object",
properties: {
allowedVars: {
type: "array",
items: { type: "string" }
},
customPatterns: {
type: "array",
items: { type: "string" }
}
},
additionalProperties: false
}
],
messages: {
sensitivePublicEnv: 'Accessing potentially sensitive environment variable "{{varName}}" that is exposed to client',
suggestServerSide: "Move this variable to server-side code or remove NEXT_PUBLIC_ prefix",
suggestAllowList: "If this variable is safe to expose, add it to allowedVars in ESLint config"
}
},
defaultOptions: [{ allowedVars: [], customPatterns: [] }],
create(context, [options]) {
const allowedVars = options.allowedVars || [];
const customPatterns = (options.customPatterns || []).map((p) => new RegExp(p, "i"));
const allPatterns = [...SENSITIVE_PATTERNS, ...customPatterns];
return {
MemberExpression(node) {
if (node.object.type === "MemberExpression" && node.object.object.type === "Identifier" && node.object.object.name === "process" && node.object.property.type === "Identifier" && node.object.property.name === "env" && node.property.type === "Identifier") {
const varName = node.property.name;
if (varName.startsWith("NEXT_PUBLIC_")) {
if (allowedVars.includes(varName)) return;
const isSensitive = allPatterns.some((pattern) => pattern.test(varName));
if (isSensitive) {
context.report({
node,
messageId: "sensitivePublicEnv",
data: { varName },
suggest: [
{
messageId: "suggestServerSide",
fix(fixer) {
return fixer.replaceText(node.property, varName.replace("NEXT_PUBLIC_", ""));
}
},
{
messageId: "suggestAllowList",
fix() {
return null;
}
}
]
});
}
}
}
}
};
}
});
// src/rules/no-unsafe-html.ts
var import_utils2 = require("@typescript-eslint/utils");
var createRule2 = import_utils2.ESLintUtils.RuleCreator(
(name) => `https://github.com/cosmstack/blackshield/blob/main/docs/rules/${name}.md`
);
var noUnsafeHtml = createRule2({
name: "no-unsafe-html",
meta: {
type: "problem",
docs: {
description: "Prevent unsafe HTML injection vulnerabilities"
},
fixable: "code",
schema: [],
messages: {
unsafeHtml: "Using dangerouslySetInnerHTML without sanitization can lead to XSS vulnerabilities",
suggestSafeHtml: "Use SafeHTML component from @cosmstack/blackshield instead",
unsafeInnerHtml: "Direct innerHTML assignment can lead to XSS vulnerabilities"
}
},
defaultOptions: [],
create(context) {
return {
JSXAttribute(node) {
if (node.name.type === "JSXIdentifier" && node.name.name === "dangerouslySetInnerHTML") {
context.report({
node,
messageId: "unsafeHtml",
suggest: [
{
messageId: "suggestSafeHtml",
fix(fixer) {
return fixer.replaceText(node.name, "html");
}
}
]
});
}
},
AssignmentExpression(node) {
if (node.left.type === "MemberExpression" && node.left.property.type === "Identifier" && node.left.property.name === "innerHTML") {
context.report({
node,
messageId: "unsafeInnerHtml"
});
}
}
};
}
});
// src/rules/index.ts
var rules = {
"no-unsafe-html": noUnsafeHtml,
"no-public-sensitive-env": noPublicSensitiveEnv
};
var configs = {
recommended: {
plugins: ["@cosmstack/blackshield/eslint-plugin"],
rules: {
"@cosmstack/blackshield/no-unsafe-html": "error",
"@cosmstack/blackshield/no-public-sensitive-env": "error"
}
}
};
var plugin = {
rules,
configs
};
var index_default = plugin;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
configs,
rules
});