Skip to content

Commit f792dd0

Browse files
authored
Fix bug where a buffer construction is attempted in env without it (#209)
* Fix bug where a buffer construction is attempted in env without it like a web browser that didn't have a system to polyfill buffer in when encountered during bundling. * Make running the integration test much easier also add integration tests to CI (this is likely to need some iteration) * Make sure stable chrome/chromedriver match in CI * Fix streaming bug, write could get blocked with no reader yet * Add some more integration test coverage of newer features * Make the integration test actually wait for elements instead of pauses so they work in CI where CPU is way more variable. Required adding some things to wait for in cases that didn't already create an element at the right time in the process.
1 parent bba034d commit f792dd0

36 files changed

Lines changed: 513 additions & 267 deletions

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,40 @@ jobs:
1818
run: |
1919
yarn
2020
yarn test
21+
22+
integration_test:
23+
runs-on: ubuntu-24.04
24+
needs: build_and_test
25+
steps:
26+
- uses: actions/checkout@v6
27+
- uses: actions/setup-node@v6
28+
with:
29+
node-version: 24.x
30+
- uses: browser-actions/setup-chrome@latest
31+
id: setup-chrome
32+
with:
33+
chrome-version: stable
34+
install-chromedriver: true
35+
- name: install dependencies
36+
run: yarn
37+
- name: write project credentials
38+
run: |
39+
echo '${{ secrets.INTEGRATION_PROJECT_JSON }}' > integration/projects/project.json
40+
echo '${{ secrets.INTEGRATION_PRIVATE_KEY }}' > integration/projects/private.key
41+
- name: start integration servers
42+
run: |
43+
yarn start &
44+
for port in 4500 4501; do
45+
for i in $(seq 1 30); do
46+
if curl -sk https://localhost:$port > /dev/null 2>&1; then
47+
echo "Server on port $port is ready"
48+
break
49+
fi
50+
sleep 1
51+
done
52+
done
53+
- name: run nightwatch tests
54+
env:
55+
CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
56+
CHROMEDRIVER_PATH: ${{ steps.setup-chrome.outputs.chromedriver-path }}
57+
run: yarn nightwatch

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ bin/
88
dist/
99
docs/
1010
coverage/
11-
integration/private.key
1211
integration/projects/*
12+
!integration/projects/*.iron
1313
integration/certs/*
1414
.direnv
15+
logs/

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ The IronWeb SDK NPM releases follow standard [Semantic Versioning](https://semve
44

55
**Note:** The patch versions of the IronWeb SDK will not be sequential and might jump by multiple numbers between sequential releases.
66

7+
## v4.3.1
8+
9+
- fix a streaming bug, encrypt could get blocked with no reader yet on writing the header+IV.
10+
- fix a bug in unmanaged encryption if ironweb was used without a bundler that could provide a `Buffer` polyfill.
11+
712
## v4.3.0
813
- add streaming encrypt and decrypt functionality for managed and unmanaged documents.
914

flake.nix

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@
55
outputs = { self, nixpkgs, flake-utils }:
66
flake-utils.lib.eachDefaultSystem (system:
77
let
8-
pkgs = nixpkgs.legacyPackages.${system};
8+
pkgs = import nixpkgs {
9+
inherit system;
10+
config.allowUnfreePredicate = pkg:
11+
builtins.elem (nixpkgs.lib.getName pkg) [ "google-chrome" ];
12+
};
913
in
1014
{
1115
devShell = pkgs.mkShell {
1216
buildInputs = [
1317
pkgs.prettier
1418
pkgs.nodejs_24
1519
(pkgs.yarn.override { nodejs = pkgs.nodejs_24; })
20+
pkgs.google-chrome
21+
pkgs.chromedriver
1622
];
1723
};
1824
});

integration/README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Integration Tests
2+
3+
## Architecture
4+
5+
The integration test setup has three components:
6+
7+
1. **Client app** (`clientHost.webpack.js`) — serves the demo app at `https://localhost:4500`. This is the UI that nightwatch interacts with.
8+
2. **Frame/Worker host** (`iclHost.webpack.js`) — serves the iframe + web worker code at `https://localhost:4501`. This is where the SDK's frame and crypto worker run, built from your local source.
9+
3. **API backend** — by default, API requests are proxied to stage ironcore-id (`https://api-staging.ironcorelabs.com`). Set `API_PROXY_TARGET=http://localhost:9090` to use a local ironcore-id instead.
10+
11+
All three SDK layers (shim, frame, worker) run locally from source, so your uncommitted changes are under test.
12+
13+
## Prerequisites
14+
15+
### Nix dev shell (recommended)
16+
17+
The project flake provides everything you need: Node.js, yarn, Chrome, and ChromeDriver (version-matched from the same nixpkgs):
18+
19+
```bash
20+
nix develop
21+
```
22+
23+
### Project credentials
24+
25+
The project credentials are encrypted with ironhide. Decrypt them before first use:
26+
27+
```bash
28+
ironhide file decrypt integration/projects/*
29+
```
30+
31+
This produces `integration/projects/project.json` and `integration/projects/private.key`, which contain the project ID, segment ID, identity assertion key ID, and signing key for the stage environment.
32+
33+
## Running the Integration App
34+
35+
```bash
36+
yarn start
37+
```
38+
39+
This generates self-signed localhost TLS certs (if not already present) and starts both webpack dev servers in parallel (client on port 4500, frame on port 4501).
40+
41+
Navigate to **`https://localhost:4500`** in your browser (the `https://` is required). If your browser shows a certificate warning for the self-signed cert, click through to proceed.
42+
43+
### Testing against a hosted frame environment
44+
45+
To test against a specific deployed frame version (e.g. to reproduce a reported bug or confirm a regression against a release), set `HOSTED_ENV` and `HOSTED_VERSION`. This loads the frame and worker from the remote environment instead of local source:
46+
47+
```bash
48+
HOSTED_VERSION=4.3.1 HOSTED_ENV=stage yarn start
49+
```
50+
51+
In this mode the client app still runs locally, but the iframe loads from the remote environment (e.g. `https://api-staging.ironcorelabs.com`). Local changes to frame or worker code will **not** be reflected — only shim changes are tested.
52+
53+
Available environments: `stage`, `dev`, `prod`.
54+
55+
### Environment variables
56+
57+
| Variable | Default | Description |
58+
|--------------------|--------------------------------------------|--------------------------------------------|
59+
| `CLIENT_HOST` | `localhost` | Hostname for the client app server |
60+
| `CLIENT_PORT` | `4500` | Port for the client app server |
61+
| `FRAME_HOST` | `localhost` | Hostname for the frame/worker server |
62+
| `FRAME_PORT` | `4501` | Port for the frame/worker server |
63+
| `CLIENT_CERT_DIR` | `certs/localhost` | TLS cert directory for the client server |
64+
| `FRAME_CERT_DIR` | `certs/localhost` | TLS cert directory for the frame server |
65+
| `API_PROXY_TARGET` | `https://api-staging.ironcorelabs.com` | Backend API to proxy `/api/1/` requests to |
66+
| `HOSTED_ENV` | _(unset)_ | Load frame from a remote env instead of local (`stage`, `dev`, `prod`) |
67+
| `HOSTED_VERSION` | _(unset)_ | SDK version string (required with `HOSTED_ENV`) |
68+
69+
## Running Nightwatch Tests
70+
71+
### 1. Start the integration app
72+
73+
```bash
74+
yarn start
75+
```
76+
77+
### 2. Run tests
78+
79+
Run all tests:
80+
81+
```bash
82+
yarn nightwatch
83+
```
84+
85+
Run tests by tag:
86+
87+
```bash
88+
yarn nightwatch --tag unmanagedEncrypt
89+
```
90+
91+
## Test Tags
92+
93+
| Tag | Test file | Description |
94+
|-----------------------------|--------------------------------------------------|--------------------------------------------------|
95+
| `unmanagedEncrypt` | `document-unmanaged-encrypt.test.js` | Unmanaged encrypt/decrypt round-trip |
96+
| `streamingEncrypt` | `document-streaming-encrypt.test.js` | Streaming encrypt/decrypt round-trip |
97+
| `unmanagedStreamingEncrypt` | `document-unmanaged-streaming-encrypt.test.js` | Unmanaged streaming encrypt/decrypt round-trip |

integration/clientHost.webpack.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ const projectIDs = require("./projects/project.json");
88
const keyFile = path.join(__dirname, "./projects/private.key");
99
const privateKey = fs.readFileSync(keyFile, "utf8");
1010

11-
const SB_HOST = "dev1.scrambledbits.org";
12-
const SB_PORT = 4500;
11+
const SB_HOST = process.env.CLIENT_HOST || "localhost";
12+
const SB_PORT = parseInt(process.env.CLIENT_PORT, 10) || 4500;
13+
const FRAME_HOST = process.env.FRAME_HOST || "localhost";
14+
const FRAME_PORT = parseInt(process.env.FRAME_PORT, 10) || 4501;
15+
const CERT_DIR = path.join(__dirname, process.env.CLIENT_CERT_DIR || "certs/localhost");
1316

1417
if ((process.env.HOSTED_VERSION && !process.env.HOSTED_ENV) || (!process.env.HOSTED_VERSION && process.env.HOSTED_ENV)) {
1518
throw new Error("In order to run against a non-local environment you need to set both the `HOSTED_VERSION` and `HOSTED_ENV` environment variables.");
@@ -26,12 +29,10 @@ function getFrameDomain() {
2629
case "dev":
2730
return "https://api-dev1.ironcorelabs.com";
2831
default:
29-
return "https://dev1.ironcorelabs.com:4501";
32+
return `https://${FRAME_HOST}:${FRAME_PORT}`;
3033
}
3134
}
3235

33-
const runtimeEnvironment = process.env.HOSTED_VERSION ? "production" : "";
34-
3536
/**
3637
* Serve the app index.html page. This figures out who the current user is, and if one doesn't exist, generates a new random user
3738
*/
@@ -72,7 +73,7 @@ function serveIndex(req, res) {
7273
*/
7374
function serveJWT(req, res) {
7475
res.json(
75-
jwt.sign({pid: projectIDs.projectId, sid: projectIDs.segmentId, kid: projectIDs.serviceKeyId || undefined}, privateKey, {
76+
jwt.sign({pid: projectIDs.projectId, sid: projectIDs.segmentId, kid: projectIDs.identityAssertionKeyId}, privateKey, {
7677
algorithm: "ES256",
7778
expiresIn: "2m",
7879
subject: req.params.userID,
@@ -92,9 +93,8 @@ module.exports = {
9293
server: {
9394
type: "https",
9495
options: {
95-
key: fs.readFileSync(path.join(__dirname, "certs/sb/privkey.pem")),
96-
cert: fs.readFileSync(path.join(__dirname, "certs/sb/cert.pem")),
97-
ca: fs.readFileSync(path.join(__dirname, "certs/sb/chain.pem")),
96+
key: fs.readFileSync(path.join(CERT_DIR, "privkey.pem")),
97+
cert: fs.readFileSync(path.join(CERT_DIR, "cert.pem")),
9898
},
9999
},
100100
setupMiddlewares: (middlewares, devServer) => {
@@ -131,7 +131,6 @@ module.exports = {
131131
},
132132
plugins: [
133133
new webpack.DefinePlugin({
134-
//"process.env.NODE_ENV": JSON.stringify(runtimeEnvironment), not sure why this conflicting problem comes up
135134
SDK_NPM_VERSION_PLACEHOLDER: JSON.stringify(process.env.HOSTED_VERSION || "SDK_NPM_VERSION_PLACEHOLDER"),
136135
_ICL_FRAME_DOMAIN_REPLACEMENT_: JSON.stringify(getFrameDomain()),
137136
}),

integration/components/UserInfo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export default class UserInfo extends React.Component<Record<string, never>, Use
5050
passcodeError: false,
5151
changingPasscode: false,
5252
listingDevices: false,
53-
loading: true,
53+
loading: false,
5454
deviceList: [],
5555
};
5656
}

0 commit comments

Comments
 (0)