Find

Article
· Jul 31 3m read

Avoiding SQL Injection in InterSystems IRIS: The Case for Secure Query Practices

SQL injection remains one of the most critical vulnerabilities in database-driven applications, allowing attackers to manipulate queries and potentially access or compromise sensitive data. In InterSystems IRIS, developers have access to both Dynamic SQL and Embedded SQL, each with distinct characteristics. Understanding how to use them securely is essential for preventing SQL injection.

The Problem: Dynamic SQL and SQL Injection

Dynamic SQL constructs queries as strings at runtime. While this offers flexibility, it also creates a vulnerability if user input is not handled correctly. For example:

Set query = "SELECT Name, Age FROM Patients WHERE Age > "_age
Set statement = ##class(%SQL.Statement).%New()
Set status = statement.%Prepare(query)

If age is user-provided, concatenating it directly into the query string exposes the application to injection. An attacker might supply a malicious value such as 0; DROP TABLE Patients, with disastrous results.

The Solution: Parameterised Queries

Parameterised queries are the best defence against SQL injection. Rather than concatenating inputs into the query, user values are bound as parameters. Here is a secure approach using Dynamic SQL:

Set query = "SELECT Name, Age FROM Patients WHERE Age > ?"
Set statement = ##class(%SQL.Statement).%New()
Set status = statement.%Prepare(query)
If status {
    Set result = statement.%Execute(age)
    While result.%Next() {
        Write "Name: ", result.Name, ", Age: ", result.Age, !
    }
}

Here, the ? placeholder ensures the age value is treated strictly as data rather than executable code, significantly reducing the risk of injection.

Embedded SQL: Built-in Safety

Embedded SQL integrates SQL directly into ObjectScript, inherently protecting against SQL injection. The host variable syntax (:variable) securely binds parameters at compile time:

&sql(SELECT Name, Age INTO :name, :age FROM Patients WHERE Age > :minAge)

With Embedded SQL, there is no mechanism to concatenate raw user input directly into the query, thereby preventing injection.

Comparing Embedded SQL and Dynamic SQL

Feature Embedded SQL Dynamic SQL
Security Safe from injection due to host variables Secure if parameterised; risky if not
Flexibility Limited (static queries only) Highly flexible for dynamic scenarios
Searchability Easy to locate in class definitions Harder to analyse; queries are in strings
Performance Compiled at class compile time Parsed and optimised at runtime

When to Use Dynamic SQL

Dynamic SQL is useful when query structures must be determined at runtime, for example when adding optional filters:

Set query = "SELECT Name, Age FROM Patients"
If includeGender {
    Set query = query_" WHERE Gender = ?"
}
Set statement = ##class(%SQL.Statement).%New()
Set status = statement.%Prepare(query)
If status {
    Set result = statement.%Execute("Male")
}

Always remember to use parameterisation (?) for these dynamically built queries to maintain security.

Conclusion

Dynamic SQL allows for flexible query building but demands responsible usage to avoid SQL injection risks. Parameterised queries address this risk effectively. Meanwhile, Embedded SQL comes with built-in safeguards, making it an excellent choice for static queries. By using these approaches appropriately, developers can build robust, secure applications with InterSystems IRIS.

Discussion (0)1
Log in or sign up to continue
Question
· Jul 31

Has anyone built a tool to generate a %Installer Manifest from an existing IRIS system?

Hi everyone,

I’m working with an existing InterSystems IRIS server that hosts several web applications and namespace-specific code and data. I’d like to reverse-engineer the current environment into a %Installer.Manifest file so I can store it in Git and manage its changes.

My goal is to:

  • Track the application setup and configuration in version control
  • Rebuild environments consistently (namespaces, CSP apps, security roles, etc.)
  • Possibly automate deployments later on

I understand that %Installer is declarative and wasn’t necessarily designed to reflect a running system. But before I start building a tool or writing scripts to extract pieces (like web apps, packages, globals, roles…), I wanted to ask:

Has anyone already built something like this — a generator, exporter, or script that helps create a %Installer manifest based on the current state of an IRIS instance?

Even partial tools, tips, or lessons learned would be greatly appreciated!

Thanks in advance,

Andre-Claude

1 Comment
Discussion (1)3
Log in or sign up to continue
Article
· Jul 31 1m read

Trois grandes tendances tech identifiées lors de READY 2025

À l’occasion d'InterSystems Ready 2025, @Guillaume Rongier a partagé sa vision de l’évolution du numérique à l’horizon 2026.

Parmi les insights clés issus de cet événement rassemblant experts et décideurs du monde entier, voici 3 grandes tendances à suivre de près :

1️⃣ L’essor d’une IA générative locale et sans friction
L'IA générative évoluera vers des LLMs locaux spécialisés et interconnectés, avec des interfaces intuitives remplaçant les prompts par des interfaces graphiques ou de l'autocomplétion.

2️⃣ L’émergence d'un nouveau métier : les “pilotes”
Les "pilotes" seront des utilisateurs intermédiaires entre développeurs et citizens développeurs, maîtrisant les outils d'IA générative pour créer des applications sans expertise logicielle approfondie.

3️⃣ Un nouveau paradigme d’architecture
Traiter les données là où elles sont stockées en rapprochant le calcul de la donnée pour gagner en performance, en sécurité et en efficacité.

Merci à tous les participants et intervenants de READY 2025 pour ces échanges riches et inspirants !

Discussion (0)1
Log in or sign up to continue
Discussion (3)2
Log in or sign up to continue
Article
· Jul 31 5m read

Introducing testcontainers-iris-node: Simplifying IRIS Integration Testing in Node.js

Overview I'm excited to announce the release of testcontainers-iris-node, a Node.js library that makes it easy to spin up temporary InterSystems IRIS containers for integration and E2E testing. This project is a natural addition to the existing family of Testcontainers adapters for IRIS, including testcontainers-iris-python and testcontainers-iris-java.

Why testcontainers-iris-node? As a Node.js developer working with InterSystems IRIS, I often faced challenges when setting up test environments that mimic production. testcontainers-iris-node solves this by leveraging the testcontainers-node framework to create isolated IRIS environments on-demand.

This is particularly valuable for:

  • Integration testing with IRIS databases
  • Testing data pipelines or microservices
  • Automating test environments in CI pipelines

Features

  • Launches IRIS in Docker containers using Testcontainers
  • Supports custom Docker images and configuration
  • Wait strategies to ensure IRIS is ready before tests begin
  • Clean teardown between test runs

Getting Started

npm install testcontainers-iris --save-dev

Example Usage

import { IRISContainer } from "testcontainers-iris";
import { createConnection } from "@intersystems/intersystems-iris-native";

const IMAGE = "containers.intersystems.com/intersystems/iris-community:latest-preview";
const container = await new IRISContainer(IMAGE).start();
const connection = createConnection(container.getConnectionOptions());
const iris = connection.createIris();
const version = iris.classMethodString("%SYSTEM.Version", "GetNumber");

How It Works Internally, the library extends GenericContainer from testcontainers, adds IRIS-specific wait strategies, and provides helper methods for connection string generation and configuration overrides.

Supported Scenarios

  • Jest or Mocha-based test suites
  • CI environments (GitHub Actions, GitLab CI, Jenkins, etc.)
  • Local development and debugging

Mocha Test Example You can also use this library for robust integration tests with Mocha. Here is an example setup:

test-setup.ts

import "source-map-support/register"
import "reflect-metadata"
import { IRISContainer, StartedIRISContainer } from "testcontainers-iris"
import { IRISNative } from "../../src"
import chai from "chai"
import sinonChai from "sinon-chai"
import chaiAsPromised from "chai-as-promised"
declare global {
    var container: StartedIRISContainer | undefined
    var connectionOptions: {
        host: string
        port: number
        user: string
        pwd: string
        ns: string
    }
}

process.env.TZ = "UTC"
chai.should()
chai.use(sinonChai)
chai.use(chaiAsPromised)

before(async () => {
    console.log("Setting up test environment...")
    const image = process.env["IRIS_IMAGE"]
    let connectionOptions = {
        host: "localhost",
        port: 1972,
        user: "_SYSTEM",
        pwd: "SYS",
        ns: "USER",
    }
    if (image) {
        const container: StartedIRISContainer = await new IRISContainer(image)
            .withNamespace("TEST")
            .start()
        console.log(`IRIS container started at ${container.getConnectionUri()}`)
        global.container = container
        connectionOptions = {
            host: container.getHost(),
            port: container.getMappedPort(1972),
            user: container.getUsername(),
            pwd: container.getPassword(),
            ns: container.getNamespace(),
        }
    }
    global.connectionOptions = connectionOptions
    IRISNative.createConnection({ ...connectionOptions, sharedmemory: false })
})

after(async () => {
    console.log("Cleaning up test environment...")
    if (global.container) {
        await global.container.stop()
    }
    delete global.container
})

Test case:

import { IRISNative, IRISConnection } from "../src/IRISNative"
describe("IRISNative test", () => {
    let connection: IRISConnection
    before(() => {
        const connectionOptions = global.connectionOptions
        connection = IRISNative.createConnection({ ...connectionOptions })
    })
    after(() => {
        if (connection) {
            connection.close()
        }
    })
    it("should work", async () => {
        const res = await connection.query(
            "SELECT 1 AS test1, '2' AS test2",
            [],
        )
        res.rows.should.be.an("array")
        res.rows.should.have.lengthOf(1)
        res.rows[0].should.be.an("object")
        res.rows[0].should.have.property("test1")
        res.rows[0].should.have.property("test2")
        res.rows[0].should.have.property("test1", 1)
        res.rows[0].should.have.property("test2", "2")
    })
})

Used in typeorm-iris This library is also used in my typeorm-iris project, which provides experimental TypeORM support for InterSystems IRIS. testcontainers-iris-node powers the integration testing setup for that project, helping validate the ORM functionality against real IRIS instances.

Library Adoption Challenge As a library adoption developer, one of my biggest challenges is testing across multiple versions of InterSystems IRIS. This tool significantly simplifies that process by allowing easy switching and automation of containerized environments with different IRIS versions.

Comparison with Other Language Bindings While testcontainers-iris-python and testcontainers-iris-java are mature and support advanced features like bind mounts and custom startup scripts, the Node.js variant is streamlined for JavaScript/TypeScript environments and aims for simplicity and developer ergonomics.

Contribute and Feedback I welcome feedback, issues, and pull requests via GitHub: testcontainers-iris-node.

Closing Thoughts testcontainers-iris-node lowers the barrier for robust, automated testing of IRIS-based applications in the Node.js ecosystem. Whether you're building APIs, ETL jobs, or integration services, this tool helps ensure confidence in your IRIS workflows.

Discussion (0)2
Log in or sign up to continue