@salla.sa/app-functions-guard
v0.1.0
Published
Static validation guard for Salla App Functions (Cloudflare Workers) bundles: dependency policy, blocked runtime APIs, outbound fetch host blocklist, and events-map/custom-event validation.
Readme
@salla.sa/app-functions-guard
Static validation guard for Salla App Functions (Cloudflare Workers) bundles.
Run it as a pre-deploy gate in CI/CD to catch disallowed dependencies, blocked runtime APIs, unsafe outbound network calls, and malformed event maps before a bundle ships — without executing any of the bundle's code.
Why this exists
App Functions run untrusted, third-party-authored code inside Salla's Cloudflare Workers
runtime. This package performs static analysis only (no eval, no sandboxed execution) so
that a bundle can be checked cheaply and safely at build/deploy time, and rejected early if
it violates platform constraints.
Checks
Four independent rules, each individually callable, and composable via guardBundle():
| Rule | File | What it does |
|---|---|---|
| npm-package-blocklist | src/rules/packageBlocklist.ts | Parses package.json dependencies and checks them against a versioned block list (src/data/runtime-package-blocklist.ts). Flags explicitly denied packages and npm packages whose names collide with blocked Node core modules. |
| runtime-api-blocklist | src/rules/runtimeApiBlocklist.ts | AST-scans source files for blocked Node built-in modules (node:fs, node:child_process, etc.) and blocked globals such as eval and process.binding. |
| outbound-blocklist | src/rules/outboundBlocklist.ts | AST-scans the bundle for fetch() calls and checks statically-resolvable hosts/paths against src/data/outbound-blocklist.ts. Calls with dynamically constructed URLs that can't be resolved at scan time are reported separately (see Static resolution limits below). |
| events-map-validation | src/rules/eventsMap.ts | Validates the bundle's events map against src/data/allowed-events.ts: loads salla.config.json first when present, validates all discovered source candidates otherwise, rejects unsupported trigger categories, and fails closed on unresolvable event declarations. |
Each rule returns a list of violations with a rule id, human-readable message, and a
severity (error | warning). guardBundle() runs all four and aggregates the results.
Install & build
npm install
npm run buildRequirements
- Node.js
>=18to use the published package - Node.js
20+recommended when contributing locally, since the lint/test toolchain targets current runtimes - A bundle directory containing
package.jsonand, optionally,salla.config.json
Programmatic usage
import { guardBundle } from '@salla.sa/app-functions-guard';
const report = guardBundle({
rootDir: '/path/to/bundle',
sourceFiles: ['index.js'],
});
if (!report.ok) {
for (const v of report.violations) {
console.error(`[${v.severity}] ${v.rule}: ${v.message}`);
}
process.exit(1);
}report.ok is false if any violation has severity: 'error'. report.violations contains
the full list from all four rules, including warnings.
Also note that warnings are informational only: they do not fail guardBundle() or the
CLI exit code. If your deployment policy treats specific warning classes as blocking, enforce
that explicitly in your CI/CD wrapper.
Running a single rule
Each rule is also exported individually, if you only need one check (e.g. inside a custom pipeline or a targeted unit test):
import {
checkPackageBlocklist,
checkRuntimeApiBlocklist,
checkOutboundBlocklist,
validateEventsMap,
} from '@salla.sa/app-functions-guard';| Export | Corresponds to rule |
|---|---|
| checkPackageBlocklist | npm-package-blocklist |
| checkRuntimeApiBlocklist | runtime-api-blocklist |
| checkOutboundBlocklist | outbound-blocklist |
| validateEventsMap | events-map-validation |
Static resolution limits
Because all checks are static (no code execution), a few edge cases are inherently out of scope:
- Dynamic URLs:
fetch(someVariable)or template-built URLs with runtime-only segments can't be resolved to a concrete host at scan time. These are reported as "unresolvable" rather than silently passed or failed — check the report for this category separately from confirmed blocklist hits. - Dynamically declared dependencies: only
package.json-declared dependencies are checked; packages pulled in via other means (e.g.require()of an unlisted path) aren't covered bynpm-package-blocklist. - Warnings are not a complete security boundary: dynamic outbound destinations and other unresolved constructs are surfaced for review, but this package should be one layer in a broader defense-in-depth deployment gate rather than the only control.
Updating the block/allow lists
The versioned data files live under src/data/:
runtime-package-blocklist.tsoutbound-blocklist.tsallowed-events.ts
Update these when Salla's platform policy changes, and bump the package version accordingly so consumers can pin to a known-good list.
