Skip to content

Commit

Permalink
Merge pull request #13 from AikidoSec/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
hansott authored Feb 14, 2024
2 parents c00f706 + a1d60c6 commit 3b4ecbd
Show file tree
Hide file tree
Showing 29 changed files with 684 additions and 644 deletions.
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ WARNING: This is an early release. Use at your own risk.

Aikido guard for Node.js is compatible with

*Express 4.x
*MongoDB 4.x, 5.x and 6.x
* ✅ Mongoose 8.x
*[`express`](https://www.npmjs.com/package/express) 4.x
*[`mongodb`](https://www.npmjs.com/package/mongodb) 4.x, 5.x and 6.x (Node.js driver version)
*[`mongoose`](https://www.npmjs.com/package/mongoose) Mongoose 8.x

## Installation

Expand Down Expand Up @@ -118,10 +118,14 @@ We believe that there are legitimate cases of prototype changes, but they should
```js
import { protect, preventPrototypePollution } from '@aikidosec/guard';

// Before main imports
protect();

import express from 'express';

// After main imports
preventPrototypePollution();

const app = express();

app.get("/", (req, res) => {
Expand All @@ -130,9 +134,6 @@ app.get("/", (req, res) => {

app.listen(3000, () => {
console.log("Server is running on port 3000");

// Your app is initialized, now it's time to prevent prototype pollution
preventPrototypePollution();
});
```

Expand Down Expand Up @@ -172,6 +173,12 @@ AIKIDO_NO_BLOCKING=true node your-app.js

See [Reporting NoSQL injections to Aikido](#reporting-nosql-injections-to-aikido) to learn how to send events to Aikido.

## Performance

We run a benchmark on every commit to make sure that the guard has a minimal impact on your application's performance.

See [benchmarks](benchmarks) for more information.

## Development

* `$ make install` to install dependencies
Expand Down
3 changes: 1 addition & 2 deletions benchmarks/express-mongodb/withGuard.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
const { protect, preventPrototypePollution } = require("@aikidosec/guard");
const { protect } = require("@aikidosec/guard");

protect();

require("./createApp")(4000).then(() => {
preventPrototypePollution();
console.log("Listening on port 4000");
console.log("Secured with @aikidosec/guard!");
});
11 changes: 11 additions & 0 deletions library/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,16 @@ module.exports = {
],
"import/no-unused-modules": ["warn", { unusedExports: true }],
"security/detect-object-injection": "off",
"no-warning-comments": "error",
"max-lines-per-function": ["error", { max: 50, skipBlankLines: true }],
"func-names": ["error", "as-needed"],
},
overrides: [
{
files: "*test.ts",
rules: {
"max-lines-per-function": "off",
},
},
],
};
7 changes: 1 addition & 6 deletions library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@
},
"dependencies": {
"require-in-the-middle": "^7.2.0",
"shimmer": "^1.2.1",
"ulid": "^2.3.0"
},
"optionalDependencies": {
"express": "^4.0.0",
"mongodb": "^4.0.0 || ^5.0.0 || ^6.0.0"
"shimmer": "^1.2.1"
}
}
6 changes: 3 additions & 3 deletions library/src/agent/API.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ function generateAttackEvent(): Event {
metadata: {},
},
agent: {
id: "id",
version: "1.0.0",
dryMode: false,
hostname: "hostname",
Expand All @@ -36,6 +35,7 @@ function generateAttackEvent(): Event {
name: "os",
version: "version",
},
serverless: false,
},
};
}
Expand Down Expand Up @@ -73,7 +73,6 @@ function generateStartedEvent(): Event {
type: "started",
time: Date.now(),
agent: {
id: "id",
version: "1.0.0",
dryMode: false,
hostname: "hostname",
Expand All @@ -85,6 +84,7 @@ function generateStartedEvent(): Event {
name: "os",
version: "version",
},
serverless: false,
},
};
}
Expand Down Expand Up @@ -119,7 +119,6 @@ function generateHeartbeatEvent(): Event {
time: Date.now(),
stats: {},
agent: {
id: "id",
version: "1.0.0",
dryMode: false,
hostname: "hostname",
Expand All @@ -131,6 +130,7 @@ function generateHeartbeatEvent(): Event {
name: "os",
version: "version",
},
serverless: false,
},
};
}
Expand Down
4 changes: 2 additions & 2 deletions library/src/agent/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export class Token {
}

export type AgentInfo = {
id: string;
dryMode: boolean;
hostname: string;
version: string;
Expand All @@ -31,6 +30,7 @@ export type AgentInfo = {
version: string;
};
nodeEnv: string;
serverless: boolean;
};

type Started = {
Expand All @@ -44,7 +44,7 @@ export type Kind = "nosql_injection";
type DetectedAttack = {
type: "detected_attack";
request: {
method: string;
method: string | undefined;
ipAddress: string | undefined;
userAgent: string | undefined;
url: string | undefined;
Expand Down
101 changes: 21 additions & 80 deletions library/src/agent/Agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,32 @@ import { hostname, platform, release } from "node:os";
import * as t from "tap";
import { Agent } from "./Agent";
import { APIForTesting, Token } from "./API";
import { IDGeneratorFixed } from "./IDGenerator";
import { LoggerNoop } from "./Logger";
import { address } from "ip";

t.test("it sends install event once", async (t) => {
t.test("it sends started event", async (t) => {
const logger = new LoggerNoop();
const api = new APIForTesting();
const token = new Token("123");
const agent = new Agent(
true,
logger,
api,
token,
[],
new IDGeneratorFixed("id"),
false
);
const agent = new Agent(true, logger, api, token, false, {
mongodb: {
version: "4.0.0",
supported: true,
},
});
agent.start();

await new Promise((resolve) => setImmediate(resolve));
t.match(api.getEvents(), [
{
type: "started",
agent: {
id: "id",
dryMode: false,
hostname: hostname(),
version: "1.0.0",
ipAddress: address(),
packages: {},
preventedPrototypePollution: false,
nodeEnv: "",
os: {
name: platform(),
version: release(),
packages: {
mongodb: "4.0.0",
},
},
},
]);

agent.start();
await new Promise((resolve) => setImmediate(resolve));
t.match(api.getEvents(), [
{
type: "started",
agent: {
id: "id",
dryMode: false,
hostname: hostname(),
version: "1.0.0",
ipAddress: address(),
packages: {},
preventedPrototypePollution: false,
nodeEnv: "",
os: {
Expand All @@ -63,46 +37,29 @@ t.test("it sends install event once", async (t) => {
},
},
]);

// Stop setInterval from heartbeat
agent.stop();
});

t.test("when prevent prototype pollution is enabled", async (t) => {
const logger = new LoggerNoop();
const api = new APIForTesting();
const token = new Token("123");
const agent = new Agent(
true,
logger,
api,
token,
[],
new IDGeneratorFixed("id"),
false
);
agent.start();
// @ts-expect-error Private property
t.same(agent.info.preventedPrototypePollution, false);
const agent = new Agent(true, logger, api, token, true, {});
agent.onPrototypePollutionPrevented();
// @ts-expect-error Private property
t.same(agent.info.preventedPrototypePollution, true);
agent.stop();
agent.start();
t.match(api.getEvents(), [
{
agent: {
preventedPrototypePollution: true,
},
},
]);
});

t.test("it does not start interval in serverless mode", async () => {
const logger = new LoggerNoop();
const api = new APIForTesting();
const token = new Token("123");
const agent = new Agent(
true,
logger,
api,
token,
[],
new IDGeneratorFixed("id"),
true
);
const agent = new Agent(true, logger, api, token, true, {});

// This would otherwise keep the process running
agent.start();
Expand All @@ -112,15 +69,7 @@ t.test("it keeps track of stats", async () => {
const logger = new LoggerNoop();
const api = new APIForTesting();
const token = new Token("123");
const agent = new Agent(
true,
logger,
api,
token,
[],
new IDGeneratorFixed("id"),
true
);
const agent = new Agent(true, logger, api, token, true, {});

agent.start();
agent.onInspectedCall({
Expand Down Expand Up @@ -176,15 +125,7 @@ t.test("it keeps tracks of stats in dry mode", async () => {
const logger = new LoggerNoop();
const api = new APIForTesting();
const token = new Token("123");
const agent = new Agent(
false,
logger,
api,
token,
[],
new IDGeneratorFixed("id"),
true
);
const agent = new Agent(false, logger, api, token, true, {});

agent.start();

Expand Down
Loading

0 comments on commit 3b4ecbd

Please sign in to comment.