Skip to content

Commit

Permalink
Merge pull request #428 from AikidoSec/graphql-tools
Browse files Browse the repository at this point in the history
Add support for @graphql-tools/executor
  • Loading branch information
hansott authored Oct 24, 2024
2 parents a0e3bc1 + d3f0808 commit 582d667
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 10 deletions.
83 changes: 83 additions & 0 deletions library/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@fastify/cookie": "^10.0.0",
"@google-cloud/functions-framework": "^3.3.0",
"@google-cloud/pubsub": "^4.3.3",
"@graphql-tools/executor": "^1.3.2",
"@hapi/hapi": "^21.3.10",
"@hono/node-server": "^1.12.2",
"@sinonjs/fake-timers": "^11.2.2",
Expand Down
93 changes: 93 additions & 0 deletions library/sources/GraphQL.tools.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as t from "tap";
import { ReportingAPIForTesting } from "../agent/api/ReportingAPIForTesting";
import { getContext, runWithContext } from "../agent/Context";
import { GraphQL } from "./GraphQL";
import { Token } from "../agent/api/Token";
import { createTestAgent } from "../helpers/createTestAgent";
import type { ExecutionArgs } from "graphql";

t.test("it works", async () => {
const agent = createTestAgent({
api: new ReportingAPIForTesting({
success: true,
endpoints: [
{
method: "POST",
route: "/graphql",
forceProtectionOff: false,
rateLimiting: {
enabled: true,
maxRequests: 3,
windowSizeInMS: 60 * 1000,
},
graphql: {
name: "getFile",
type: "query",
},
},
],
allowedIPAddresses: [],
configUpdatedAt: 0,
heartbeatIntervalInMS: 10 * 60 * 1000,
blockedUserIds: [],
}),
token: new Token("123"),
});

agent.start([new GraphQL()]);

const { parse, buildSchema } = require("graphql");
const { execute } = require("@graphql-tools/executor");

const schema = buildSchema(`
type Query {
getFile(path: String): String
}
`);

const root = {
getFile: ({ path }: { path: string }) => {
return "file content";
},
};

const query = async (path: string) => {
const executionArgs: ExecutionArgs = {
schema,
document: parse(`{ getFile(path: "${path}") }`),
rootValue: root,
};

return execute(executionArgs);
};

const context = {
remoteAddress: "::1",
method: "POST",
url: "http://localhost:4000/graphql",
query: {},
headers: {},
body: { query: '{ getFile(path: "/etc/bashrc") }' },
cookies: {},
routeParams: {},
source: "express",
route: "/graphql",
};

await query("/etc/bashrc");

await runWithContext(context, async () => {
await query("/etc/bashrc");
t.same(getContext()?.graphql, ["/etc/bashrc"]);
});

// Rate limiting works
await runWithContext(context, async () => {
const success = await query("/etc/bashrc");
t.same(success.data.getFile, "file content");
await query("/etc/bashrc");
await query("/etc/bashrc");
const result = await query("/etc/bashrc");
t.same(result.errors[0].message, "You are rate limited by Zen.");
});
});
31 changes: 21 additions & 10 deletions library/sources/GraphQL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Agent } from "../agent/Agent";
import { getContext, updateContext } from "../agent/Context";
import { Hooks } from "../agent/hooks/Hooks";
import { WrapPackageInfo } from "../agent/hooks/WrapPackageInfo";
import { Wrapper } from "../agent/Wrapper";
import type { ExecutionArgs } from "graphql/execution/execute";
import { isPlainObject } from "../helpers/isPlainObject";
Expand Down Expand Up @@ -100,21 +101,31 @@ export class GraphQL implements Wrapper {
return origReturnVal;
}

wrap(hooks: Hooks) {
const methods = ["execute", "executeSync"] as const;
private wrapExecution(exports: unknown, pkgInfo: WrapPackageInfo) {
const methods = ["execute", "executeSync"];

for (const method of methods) {
wrapExport(exports, method, pkgInfo, {
modifyReturnValue: (args, returnValue, agent) =>
this.handleRateLimiting(args, returnValue, agent),
inspectArgs: (args, agent) => this.inspectGraphQLExecute(args, agent),
});
}
}

wrap(hooks: Hooks) {
hooks
.addPackage("graphql")
.withVersion("^16.0.0")
.onFileRequire("execution/execute.js", (exports, pkgInfo) => {
for (const method of methods) {
wrapExport(exports, method, pkgInfo, {
modifyReturnValue: (args, returnValue, agent) =>
this.handleRateLimiting(args, returnValue, agent),
inspectArgs: (args, agent) =>
this.inspectGraphQLExecute(args, agent),
});
}
this.wrapExecution(exports, pkgInfo);
});

hooks
.addPackage("@graphql-tools/executor")
.withVersion("^1.0.0")
.onFileRequire("cjs/execution/execute.js", (exports, pkgInfo) => {
this.wrapExecution(exports, pkgInfo);
});
}
}

0 comments on commit 582d667

Please sign in to comment.