PSA/eslint-plugin-custom-rules/no-legacy-ext-imports.js
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

118 lines
3.5 KiB
JavaScript

const FORBIDDEN_SUBSTRINGS = [
"ee/server/src/lib/extensions/ui/descriptors/",
"ee/server/src/lib/extensions/ui/pages/",
"ee/server/src/lib/extensions/ui/tabs/",
"ee/server/src/lib/extensions/ui/navigation/",
"ee/server/src/lib/extensions/security/propWhitelist.ts",
"ee/server/src/lib/extensions/schemas/manifest.schema.ts",
"ee/server/src/lib/extensions/schemas/extension-points.schema.ts",
"ee/server/src/lib/extensions/validator.ts",
"/api/extensions/",
];
// Normalize path separators for safety across OS
function normalizePath(value) {
if (typeof value !== "string") return value;
return value.replace(/\\+/g, "/");
}
function isForbidden(value) {
if (typeof value !== "string") return false;
const v = normalizePath(value);
return FORBIDDEN_SUBSTRINGS.some((needle) => v.includes(needle));
}
const MESSAGE =
"Legacy extension system import is forbidden; use v2 APIs (Runner/Gateway/ManifestV2).";
export default {
meta: {
type: "problem",
docs: {
description:
"Disallow imports/usages from legacy extension system paths. Use v2 APIs instead.",
recommended: true,
},
schema: [], // no options
messages: {
forbiddenLegacy: MESSAGE,
},
},
create(context) {
return {
// import foo from "module";
// import("module")
ImportDeclaration(node) {
const sourceVal = node?.source?.value;
if (isForbidden(sourceVal)) {
context.report({
node: node.source,
messageId: "forbiddenLegacy",
});
}
},
// require("module")
CallExpression(node) {
try {
const callee = node.callee;
const isRequire =
callee &&
callee.type === "Identifier" &&
callee.name === "require";
if (isRequire && node.arguments && node.arguments.length) {
const arg = node.arguments[0];
if (arg && arg.type === "Literal" && typeof arg.value === "string") {
if (isForbidden(arg.value)) {
context.report({
node: arg,
messageId: "forbiddenLegacy",
});
}
}
}
} catch {
// noop
}
},
// import("module") as ImportExpression (ESTree for dynamic import)
ImportExpression(node) {
const src = node.source;
if (src && src.type === "Literal" && typeof src.value === "string") {
if (isForbidden(src.value)) {
context.report({
node: src,
messageId: "forbiddenLegacy",
});
}
}
},
// Bare string literals anywhere in source (to catch raw "/api/extensions/" usage)
// Note: This purposely may be broad to ensure no raw usage sneaks in.
Literal(node) {
if (typeof node.value === "string" && isForbidden(node.value)) {
// Avoid double-reporting on import literals already handled above by checking parent types
const parentType = node.parent && node.parent.type;
const parentIsImport =
parentType === "ImportDeclaration" ||
parentType === "ImportExpression" ||
(parentType === "CallExpression" &&
node.parent.callee &&
node.parent.callee.type === "Identifier" &&
node.parent.callee.name === "require");
if (!parentIsImport) {
context.report({
node,
messageId: "forbiddenLegacy",
});
}
}
},
};
},
};