Most frameworks treat injection attacks as problems to be mitigated: sanitise inputs, escape outputs, document what not to do, and hope developers follow the rules consistently. FOAM takes a different approach — it eliminates the attack surface at the architectural level so that the normal API provides no path to injection in the first place.
U2, FOAM’s UI library, was designed in conjunction with Google’s security team during the Chrome Apps era. Chrome Apps enforced a strict security model that prohibited three things:
eval() — no runtime code generation from strings<script> tags in <body> — no inline script injectiononclick="...") — no HTML-level callbacksThese constraints forced a fully component-based, code-wired architecture. U2 was built to satisfy all three by construction:
this.start().add().end()) never concatenates strings into HTML. Content is always set as text nodes or DOM properties — never parsed as markup.The result is that XSS is structurally impossible in a FOAM application. User input handled through the normal U2 API cannot become executable markup, because U2 never hands strings to the HTML parser.
Chrome Apps no longer exist, but FOAM retains these constraints through a self-imposed Content Security Policy. The security properties remain, independent of the platform that originally motivated them.
The same principle — build from objects, not strings — applies one layer down at the query layer.
SQL injection works because queries are built by string concatenation: user input lands inside the string, the database parser cannot distinguish data from syntax, and the injection succeeds. MLang makes this structurally impossible:
M.AND(
M.EQ(Invoice.CUSTOMER_ID, customerId), // a typed value, not a string fragment
M.GT(Invoice.AMOUNT, userInput) // typed and bounds-checked, not interpolated
)
There is no query string to inject into. The predicate tree is a graph of typed objects. When a ClientDAO sends a query over the network, it serialises the predicate tree to JSON and reconstructs it server-side — the server never receives a raw query string that user input could escape. When a JDBCDAO executes against a SQL database, it generates parameterised queries from the predicate tree, with data always kept separate from query structure.
Both protections follow from the same architectural decision made throughout FOAM: use typed object composition rather than string interpolation at every layer. There is no injection surface because there are no strings being parsed as code or markup anywhere in the normal execution path.
| Layer | Conventional risk | FOAM’s approach | Result |
|---|---|---|---|
| UI rendering | XSS via template injection | Builder API sets DOM properties, never innerHTML | XSS structurally impossible |
| Query construction | SQL injection via string concatenation | MLang predicate objects, parameterised execution | SQL injection structurally impossible |
| Runtime code | Code injection via eval | No eval, strict CSP | eval-based injection impossible |
FOAM application security centers around services and permissions to those services.
Services are known as CORE Services and their access configuration is modelled under the name CSpec (CORE Service Specification). The CSpec controls how a service is exposed to the rest of the system.
doc/guides/Services.md for documenation regarding Services themselves.doc/guides/Permissions.md for documentation regarding Permissions themselves.- see src/foam/core/auth/README.md for documentation on the Authentication system itself - how authentication is implemented and how permission checking occurs. Overlaps with this documentation.CSpecs are deployed in services.jrl journals and can also be created and manipulated at runtime.
foam/src/services.jrl for many examples.Services, by default, are only accessible within the server application. They can be exposed to clients with CSpec property serve.
Services which are served, are authenticated. Authentication is enabled by default via CSpec property authenticate.
A client’s permissions are those of the *user that is associated with the session of the client connection.*
Autenticated services require the client (connecting user) to have permission
service.read.<cspec-name><cspec.name>service.<cspec-name> (legacy)Unauthenticated services are open to all clients for reading.
Once a client connects to a service, security managing data manipulation is controlled at the model level (discussed below in Model Security).
The service that the CSpec describes is specified by one of the following properties, which resolve to a modelled class.
When the service is served, then the client property must be configured.
Additionally if the service is modelled as an INTERFACE, then boxClass property must also be provided. See /#flowdoc/Boxes.flow for a discussion of FOAM’s Box messaging protocol.
When the service is a DAO, then typically, a serviceScript is used to describe the DAO stack with EasyDAO (discussed below).
Model security applies to both client and server operations, and is independent of the service’s accessibility.
Model security is implemented via permissions. The permissins are imposed at two levels: 1) the model itself controlling instance visibility and manipulation, and 2) model properties controlling visibility and manipulation of individual properties.
Model level authentication is imposed via AuthorizationDAO, verifying the user has appropriate permissions for each CRUD operation.
Two interfaces control how the AuthorizationDAO is configured for each service:
AuthorizableAuthorizer which delegates to the model for permission checking.Authorizable when requiring behaviour different from the StandardAuthorizer.
foam.core.auth.User as an example.Typically the AuthorizationDAO is configured by EasyDAO.
EasyDAO is a support model designed to hide the complexities of, correctly and securely, configuring DAO decoration for a service (DAO stack).
EasyDAO properties relevant to Model level authentication:
Model property authentication allows for fine grained per-property permissioning. It is imposed by PermissionedPropertyDAO.
Model property permissioning affects both client and server.
Typically, PermissionedPropertyDAO is configured by EasyDAO, with property permissioned.
EasyDAO properties relevant to Model level authentication:
puts replace the incomming value with the previous value, nullify any change.finds replace the current property value with it’s default.puts replace the incomming value with the previous value, nullify any change.foam.u2.Element2.js:1767, foam/core/auth/PermissionedPropertyDAO.js:118 & 222authenticate on the cspec do? Is it safe to set it false?
authenticate comes into play when a CSpec is served (serve:true). A served service is exposed to clients and by default is authenticated. Authentication requires the user to have permission service.<CSpec.name>. An unauthenticated service (authenticate:false), will be visible to all clients.authenticate:false and EasyDAO authorize:false.authenticate:false, makes the service accessible to all clients.service.<modelDAO> and model.read.* are required. If EasyDAO authorizer is configured with the GlobalReadAuthoriizer, then all clients can read all objects, but permissions to create, update, delete (ex. model.update.*) are required.foam.core.auth.Authorizable and implement each of the authorizeOn... methods.foam.core.auth.Authorizable or, for a DAO, EasyDAO.authorize is enabled (which it is by default), then any read request by a client must have permission <modelname>.read.* or one or more <modelname>.read.id for each id of interest.model.read.* is a wildcard permission which allows a user to see all ids (all instances) of the model in question. By default a user is restricted to only see instances they have been given explicit access to via a permission of the form model.read.id.Auth.sudo is used when logic requires reduced scoped or scope of another user (other than system). For example, Approvals, approved by an operator but when put, the context of the user that the Approval affects is used so that rules of the post approval flow execute in the user’s scope.src/foam/java/Skeleton.js:157