Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: SQL Injection Prevention (Flow Simulation) #23

Merged
merged 3 commits into from Aug 18, 2022

Conversation

kokokuo
Copy link
Contributor

@kokokuo kokokuo commented Jul 20, 2022

Description

Support preventing sql injection attack.

For the detailed description and introduction, please read Notion link !

🔔 Notice: The PR is only to simulates the getting data source and re-parsing parameters flow and add re-parse method in the interface, the really logistic should wait data source to implement, then could be able to replace it.

How To Test / Expected Results

For the test result, please see the below test cases that passed the unit test:

Core package

螢幕快照 2022-08-15 下午8 34 32

Serve package

螢幕快照 2022-08-15 下午8 35 28

Integration testing package

螢幕快照 2022-08-15 下午8 36 13

Commit Message

  • 90b3942 - feat(core, serve): add sql injection prevention structure and flow.
    • add prepare parameters method in data source interface.
    • inject data source in route generator
    • refactor template engine for re-wrap data to context object.
    • update req tag runner to get bind paramters from context and send to executor for creating builder.
    • add identifier in data query builder
    • update test cases for data query builder and template engine.
    • update route generator, app, server test cases.

@kokokuo kokokuo changed the base branch from develop to feature/response-format July 20, 2022 08:53
@kokokuo kokokuo changed the title Feature/prevent sql injection Feature: SQL Injection Prevention ( Add method contract and flow Simulation ) Jul 20, 2022
@kokokuo kokokuo changed the title Feature: SQL Injection Prevention ( Add method contract and flow Simulation ) Feature: SQL Injection Prevention ( Flow Simulation ) Jul 20, 2022
@kokokuo kokokuo marked this pull request as ready for review July 20, 2022 08:55
@kokokuo kokokuo requested a review from oscar60310 July 20, 2022 08:55
@kokokuo kokokuo changed the title Feature: SQL Injection Prevention ( Flow Simulation ) Feature: SQL Injection Prevention (Flow Simulation) Jul 20, 2022
@kokokuo kokokuo changed the title Feature: SQL Injection Prevention (Flow Simulation) [WIP] Feature: SQL Injection Prevention (Flow Simulation) Jul 25, 2022
@kokokuo kokokuo changed the title [WIP] Feature: SQL Injection Prevention (Flow Simulation) Feature: SQL Injection Prevention (Flow Simulation) Jul 25, 2022
@kokokuo kokokuo force-pushed the feature/prevent-sql-injection branch from 1a337d6 to e2fe9a4 Compare July 28, 2022 07:06
Base automatically changed from feature/response-format to develop August 2, 2022 07:32
- add prepare parameters method in data source interface.
- inject data source in route generator
- refactor template engine for re-wrap data to context object.
- update req tag runner to get bind paramters from context and send to executor for creating builder.
- add identifier in data query builder
- update test cases for data query builder and template engine.
- update route generator, app, server test cases.
@kokokuo kokokuo force-pushed the feature/prevent-sql-injection branch from e2fe9a4 to 90b3942 Compare August 15, 2022 12:31
@kokokuo kokokuo changed the base branch from develop to feature/extension-loader August 15, 2022 12:32
@kokokuo
Copy link
Contributor Author

kokokuo commented Aug 16, 2022

Rebase finished from #25.

Base automatically changed from feature/extension-loader to develop August 17, 2022 06:45
Copy link
Contributor

@oscar60310 oscar60310 left a comment

Choose a reason for hiding this comment

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

Other part LGTM.

Just a kindly reminder: We have a tradeoff when using prepared parameters: we won’t able to take operation on parameters anymore.

select * from users where age > {{ context.params.age + 20 }}

--
age=18
select * from users where age > 38

// handle parameterized query statement
let query = statement;
for (const identifier of Object.keys(bindParams)) {
query = query.replace(identifier, bindParams[identifier]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should use global searching instead:

Suggested change
query = query.replace(identifier, bindParams[identifier]);
query = query.replace(new RegExp(identifier, 'g') , bindParams[identifier]);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for reviewing! it has been fixed.

@@ -75,6 +78,7 @@ describe('Test data query builder > where clause', () => {
const builder = new DataQueryBuilder({
statement: 'select * from orders',
dataSource: stubDataSource,
bindParams: sinon.stubInterface<BindParameters>(),
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: I'd prefer to create a helper function like createMockBuild to prevent potential changes in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for suggesting it! it has been refactored.

@@ -304,6 +310,7 @@ describe('Test data query builder > having clause', () => {
const builder = new DataQueryBuilder({
statement: 'select * from orders',
dataSource: stubDataSource,
bindParams: sinon.stubInterface<BindParameters>(),
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: We used to stub at the arranging phase.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for suggesting it! it has been refactored, I use the createStub function like above you suggested to handle it.

Comment on lines 36 to 37
const binds = (context.ctx?.context || {})['_paramBinds'] || {};
const builder = await this.executor.createBuilder(query, binds);
Copy link
Contributor

Choose a reason for hiding this comment

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

How about adding a testing case to cover your change:

  // Arrange
  const { compiler, loader, builder, executor } = await createTestCompiler();
  const { compiledData } = await compiler.compile(`
{% req userCount main %}
select count(*) as count from user where user.id = '{{ context.params.userId }}';
{% endreq %}
  `);
  builder.value.onFirstCall().resolves([{ count: 1 }]);
  // Action
  loader.setSource('test', compiledData);
  await compiler.execute('test', {
    context: { params: { userId: '@userId' } },
    ['_paramBinds']: { '@userId': '000000' },
  });
  // Assert
  expect(executor.createBuilder.firstCall.args[0]).toBe(
    `select count(*) as count from user where user.id = '@userId';`
  );
  expect(executor.createBuilder.firstCall.args[1]).toEqual({
    '@userId': '000000',
  });

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for suggesting and providing the complete test cases ❤️

Comment on lines 72 to 82
return this.compiler.execute(
templateName,
{
context: {
...others,
['params']: prepared?.identifiers || {},
['_paramBinds']: prepared?.binds || {},
},
},
pagination
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we don't want users to access _paramBinds via templates, how about moving it out of context properties?

Suggested change
return this.compiler.execute(
templateName,
{
context: {
...others,
['params']: prepared?.identifiers || {},
['_paramBinds']: prepared?.binds || {},
},
},
pagination
);
return this.compiler.execute(
templateName,
{
context: {
...others,
['params']: prepared?.identifiers || {},
},
['_paramBinds']: prepared?.binds || {},
},
pagination
);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for suggesting, you're right, it will be better to move out of context, it has been fixed!

@@ -37,11 +38,13 @@ export class RouteGenerator {
@inject(TYPES.RequestValidator) reqValidator: IRequestValidator,
@inject(TYPES.PaginationTransformer)
paginationTransformer: IPaginationTransformer,
@inject(CORE_TYPES.DataSource) dataSource: DataSource,
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: We have more and more dependencies to inject, I think it is better to add RestfulRoute and GraphQLRoute to container and inject them to RouteGenerator directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for suggesting, I agree with your idea, but it needs some time to refactor ( maybe need to use AutoNamedFactory ), I would like to refactor it with a new feature together :)

@kokokuo
Copy link
Contributor Author

kokokuo commented Aug 17, 2022

Other part LGTM.

Just a kindly reminder: We have a tradeoff when using prepared parameters: we won’t able to take operation on parameters anymore.

select * from users where age > {{ context.params.age + 20 }}

--
age=18
select * from users where age > 38

Thanks for reminding me~, I think we could solve the case in the future with new design ?

@kokokuo
Copy link
Contributor Author

kokokuo commented Aug 17, 2022

Hi @oscar60310 besides the last NIT I would like to handle it in the new feature (need some time to refactor and test), others are fixed, thanks so much!

@oscar60310 oscar60310 merged commit 0b1f30b into develop Aug 18, 2022
@oscar60310 oscar60310 deleted the feature/prevent-sql-injection branch August 18, 2022 01:49
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.

None yet

2 participants