Skip to content

Comments

🛡️ Sentinel: [CRITICAL] Fix SQL injection in agent import#84

Draft
Dexploarer wants to merge 1 commit intodevelopfrom
sentinel-fix-memory-table-injection-4327353746641705221
Draft

🛡️ Sentinel: [CRITICAL] Fix SQL injection in agent import#84
Dexploarer wants to merge 1 commit intodevelopfrom
sentinel-fix-memory-table-injection-4327353746641705221

Conversation

@Dexploarer
Copy link
Owner

🛡️ Sentinel: [CRITICAL] Fix SQL Injection in Agent Import

🚨 Severity: CRITICAL
💡 Vulnerability: The importAgent function in src/services/agent-export.ts was vulnerable to SQL injection. It allowed arbitrary strings from the type field of imported memory objects to be used as table names in database queries (db.createMemory). An attacker could craft a malicious .eliza-agent file with a SQL payload in the type field (e.g., "messages; DROP TABLE users; --") which would be executed by the database adapter.
🎯 Impact: This could allow an attacker to delete tables, exfiltrate data, or execute arbitrary SQL commands if they can trick a user into importing a malicious agent file.
🔧 Fix: Modified resolveMemoryTableName in src/services/agent-export.ts to strictly validate the resolved table name against a whitelist of known tables (MEMORY_TABLES). If the type is not in the whitelist, it falls back to "messages".
✅ Verification: Added a new security regression test src/services/agent-export-security.test.ts that attempts to import a memory with a malicious table name. The test confirms that the malicious name is not passed to the database adapter. Existing tests in src/services/agent-export.test.ts were also run to ensure no regressions.


PR created automatically by Jules for task 4327353746641705221 started by @Dexploarer

@google-labs-jules
Copy link

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai
Copy link

coderabbitai bot commented Feb 17, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sentinel-fix-memory-table-injection-4327353746641705221

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @Dexploarer, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a critical security patch to prevent SQL injection during the agent import process. By implementing strict validation for memory table names, it safeguards the system from potential data exfiltration, deletion, or arbitrary SQL command execution via crafted agent files.

Highlights

  • Critical SQL Injection Fix: Addressed a critical SQL injection vulnerability in the importAgent function within src/services/agent-export.ts. Previously, arbitrary strings from the type field of imported memory objects could be used as table names, allowing malicious SQL payloads.
  • Enhanced Table Name Validation: Modified the resolveMemoryTableName function to strictly validate resolved table names against a whitelist of known tables (MEMORY_TABLES). If a type is not whitelisted, it now defaults to 'messages', preventing injection.
  • New Security Regression Test: Introduced a new security regression test in src/services/agent-export-security.test.ts to verify that malicious table names are not passed to the database adapter during agent import.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/api/cloud-routes.test.ts
    • Reformatted function calls in several test cases for improved readability.
  • src/api/database.test.ts
    • Adjusted formatting in mock pg pool and dns.lookup implementations, and test assertions.
  • src/services/agent-export-security.test.ts
    • Added a dedicated security test file to verify protection against SQL injection in agent import, specifically targeting malicious memory table names.
  • src/services/agent-export.ts
    • Modified resolveMemoryTableName to validate memory types against a predefined whitelist (MEMORY_TABLES), preventing arbitrary strings from being used as table names during agent import.
  • test/api-security-headers.e2e.test.ts
    • Applied minor formatting adjustments to security header test assertions.
Activity
  • PR created automatically by Jules for task 4327353746641705221, initiated by @Dexploarer.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

);
});

it("GET /api/cloud/agents lists agents", async () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test for GET /api/cloud/login/status sets environment variables (process.env.ELIZAOS_CLOUD_API_KEY and process.env.ELIZAOS_CLOUD_ENABLED) but does not clean them up after the test. This can lead to test pollution and unpredictable results in subsequent tests that may depend on these variables.

Recommended solution:
Add cleanup logic in the afterEach block to unset or restore these environment variables:

const originalEnv = { ...process.env };
beforeEach(() => { /* ... */ });
afterEach(() => { process.env = { ...originalEnv }; });

Or explicitly delete the variables set during the test.

Comment on lines 148 to 164
it("POST /api/database/query rejects mutation keywords in read-only mode", async () => {
const { req, res } = createMocks("POST", "/api/database/query", {
sql: "DELETE FROM users",
});

await handleDatabaseRoute(req, res, { adapter: {} } as any, "/api/database/query");
await handleDatabaseRoute(
req,
res,
{ adapter: {} } as any,
"/api/database/query",
);

expect(res.statusCode).toBe(400);
expect(res.end).toHaveBeenCalledWith(expect.stringContaining("mutation keyword"));
expect(res.end).toHaveBeenCalledWith(
expect.stringContaining("mutation keyword"),
);
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutation Keyword Detection May Be Too Simplistic

The test for mutation keyword rejection in read-only mode (lines 148-164) only checks for the presence of 'DELETE' in the SQL string. This approach may not catch all mutation attempts, such as case variations (delete), comments, or obfuscated SQL. Consider enhancing the mutation keyword detection logic to use a case-insensitive search and to parse SQL more robustly, possibly using a SQL parser library.

Example improvement:

const mutationRegex = /\b(INSERT|UPDATE|DELETE|DROP|ALTER|TRUNCATE)\b/i;
if (mutationRegex.test(sql)) {
  // reject
}

This would improve security and reliability of the read-only enforcement.

Comment on lines +241 to +244
if (call?.tableName === maliciousType) {
throw new Error(
`VULNERABILITY DETECTED: createMemory called with malicious table name: "${call.tableName}"`,
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Issue: Test expects vulnerability to exist, but does not explicitly fail if the malicious table name is used.

Currently, the test throws an error if the malicious table name is passed to createMemory, but this is not an explicit test failure. It would be clearer and more robust to use expect(call?.tableName).not.toBe(maliciousType) to ensure the test fails in a standard way if the vulnerability is present.

Recommended solution:
Replace the manual error throw with a standard assertion:

expect(call?.tableName).not.toBe(maliciousType);

This will make the test result clearer and easier to interpret.

Comment on lines 732 to 733
return "messages";
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Data Misclassification:
The function defaults to returning 'messages' when the memory type is unrecognized or missing. This silent fallback may lead to misclassification of memory records, especially if new types are introduced or if malformed records are present. Consider logging a warning or throwing an error when an unknown type is encountered to improve robustness and maintain data integrity.

Recommended Solution:

if (typeof memType === "string" && memType.length > 0) {
  if ((MEMORY_TABLES as readonly string[]).includes(memType)) {
    return memType;
  } else {
    logger.warn(`Unknown memory type: ${memType}. Defaulting to 'messages'.`);
  }
}
return "messages";

Alternatively, throw an error if strictness is desired.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses a critical SQL injection vulnerability in the agent import functionality. The fix correctly validates the memory table name against a whitelist, preventing malicious table names from being used in database queries. A new security regression test has been added to verify this fix. The changes look good and effectively mitigate the vulnerability. I have one suggestion to improve the clarity and assertions in the new security test.

Comment on lines +221 to +245
// We expect this to either fail (if we fix it) or succeed (vulnerable)
// Currently vulnerable:
try {
await importAgent(targetRuntime, fileBuffer, password);
} catch (e) {
// If it throws "Invalid table name", we are good.
if (e.message.includes("Invalid memory table name")) {
return; // Pass
}
throw e;
}

// If we reach here, check what table name was passed to createMemory
const call = targetDb.createMemoryCalls.find(
(c) => c.memory.content.text === "evil",
);
expect(call).toBeDefined();

// Assert that the table name is NOT the malicious one
// If it IS the malicious one, the test fails (demonstrating vulnerability)
if (call?.tableName === maliciousType) {
throw new Error(
`VULNERABILITY DETECTED: createMemory called with malicious table name: "${call.tableName}"`,
);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The test logic can be simplified and made more explicit. The current implementation of the fix in resolveMemoryTableName falls back to a default table name ("messages") instead of throwing an error. The try...catch block is therefore not necessary and can be confusing.

Additionally, the assertion at the end can be more specific by checking that the table name is indeed the expected fallback value, rather than just checking that it's not the malicious one. Using expect from vitest will also provide better test failure messages.

Suggested change
// We expect this to either fail (if we fix it) or succeed (vulnerable)
// Currently vulnerable:
try {
await importAgent(targetRuntime, fileBuffer, password);
} catch (e) {
// If it throws "Invalid table name", we are good.
if (e.message.includes("Invalid memory table name")) {
return; // Pass
}
throw e;
}
// If we reach here, check what table name was passed to createMemory
const call = targetDb.createMemoryCalls.find(
(c) => c.memory.content.text === "evil",
);
expect(call).toBeDefined();
// Assert that the table name is NOT the malicious one
// If it IS the malicious one, the test fails (demonstrating vulnerability)
if (call?.tableName === maliciousType) {
throw new Error(
`VULNERABILITY DETECTED: createMemory called with malicious table name: "${call.tableName}"`,
);
}
// We expect the import to succeed, but the malicious table name should be sanitized.
await importAgent(targetRuntime, fileBuffer, password);
// Check what table name was passed to createMemory
const call = targetDb.createMemoryCalls.find(
(c) => c.memory.content.text === "evil",
);
expect(call).toBeDefined();
// Assert that the table name is the safe fallback ("messages") and not the malicious one.
expect(call?.tableName).toBe("messages");
expect(call?.tableName).not.toBe(maliciousType);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant