Skip to content

Commit

Permalink
Merge pull request liferay#1008 from tinycarol/IFI-3334
Browse files Browse the repository at this point in the history
feat(eslint-plugin): add ESLint rule to prevent usage of document.cookie
  • Loading branch information
bryceosterhaus authored Sep 27, 2022
2 parents 9819a93 + 64844e7 commit 7aa9ea3
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 0 deletions.
1 change: 1 addition & 0 deletions projects/eslint-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ The bundled `@liferay` plugin includes the following [rules](./rules/general/doc
The bundled `@liferay/portal` plugin includes the following [rules](./rules/portal/docs/rules):

- [@liferay/portal/deprecation](./rules/portal/docs/rules/deprecation.md): Enforces standard formatting of `@deprecated` annotations.
- [@liferay/portal/no-document-cookie](./rules/portal/docs/rules/no-document-cookie.md): Prevents saving and reading cookies without user consent.
- [@liferay/portal/no-explicit-extend](./rules/portal/docs/rules/no-explicit-extend.md): Prevents unnecessary extensions in ESLint and Babel configuration files.
- [@liferay/portal/no-global-fetch](./rules/portal/docs/rules/no-global-fetch.md): Prevents usage of unwrapped fetch to avoid possible issues related to security misconfiguration.
- [@liferay/portal/no-loader-import-specifier](./rules/portal/docs/rules/no-loader-import-specifier.md): Ensures that ".scss" files imported via the loader are used only for side-effects.
Expand Down
1 change: 1 addition & 0 deletions projects/eslint-plugin/configs/portal.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const config = {
rules: {
'@liferay/portal/deprecation': 'error',
'@liferay/portal/no-default-export-from-frontend-js-web': 'error',
'@liferay/portal/no-document-cookie': 'error',
'@liferay/portal/no-explicit-extend': 'error',
'@liferay/portal/no-global-fetch': 'error',
'@liferay/portal/no-loader-import-specifier': 'error',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Disallow use of document.cookie (no-document-cookie)

This rule guards against the direct use of the global cookie API in `document.cookie`. To comply with data protection regulations, Liferay users can disable the storage of data that is not fundamental for the main purpose of the site to work, and that applies to cookies. Both the global Liferay object and the `frontend-js-web` module offer a thin wrapper around this API with added user consent enforcement.

## Rule Details

Examples of **incorrect** code for this rule:

```js
function doSomething(name) {
return document.cookie
.split('; ')
.find((v) => v.startsWith(name))
?.split('=')[0];
}

function doSomethingElse(name, value) {
document.cookie += `${name}=${value}`;
}
```
Examples of **correct** code for this rule:
```js
import {getCookie, setCookie, COOKIE_TYPES} from 'frontend-js-web';

function doSomething(name) {
return getCookie(name, COOKIE_TYPES.NECESSARY);
}

function doSomethingElse(name, value, expires) {
return setCookie(name, value, COOKIE_TYPES.FUNCTIONAL, {expires});
}

function doSomethingOther(name, value) {
return Liferay.Util.Cookie.set(
name,
value,
Liferay.Util.Cookie.TYPES.PERSONALIZATION
);
}
```
## Further Reading
- [LPS-151966 Create a JS API for cookie management, which enforces user's consent](https://issues.liferay.com/browse/LPS-151966)
1 change: 1 addition & 0 deletions projects/eslint-plugin/rules/portal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
module.exports = {
'portal/deprecation': require('./lib/rules/deprecation'),
'portal/no-default-export-from-frontend-js-web': require('./lib/rules/no-default-export-from-frontend-js-web'),
'portal/no-document-cookie': require('./lib/rules/no-document-cookie'),
'portal/no-explicit-extend': require('./lib/rules/no-explicit-extend'),
'portal/no-global-fetch': require('./lib/rules/no-global-fetch'),
'portal/no-loader-import-specifier': require('./lib/rules/no-loader-import-specifier'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* SPDX-FileCopyrightText: © 2017 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/

const DESCRIPTION =
'Direct usage of `document.cookie` is discouraged in favour of our wrapped version that checks user consent status; import `[get|set|remove]Cookie` from frontend-js-web instead or use the global `Liferay.Util.Cookie`.';

module.exports = {
create(context) {
const isDocumentCookie = (node) =>
node.object.name === 'document' && node.property.name === 'cookie';

return {
MemberExpression(node) {
if (isDocumentCookie(node)) {
context.report({
messageId: 'noDocumentCookie',
node,
});
}
},
};
},

meta: {
docs: {
category: 'Best Practices',
description: DESCRIPTION,
recommended: false,
url: 'https://issues.liferay.com/browse/IFI-3334',
},
fixable: null,
messages: {
noDocumentCookie: DESCRIPTION,
},
schema: [],
type: 'problem',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* SPDX-FileCopyrightText: © 2017 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/

const MultiTester = require('../../../../../scripts/MultiTester');
const rule = require('../../../lib/rules/no-document-cookie');

const parserOptions = {
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
},
};

const ruleTester = new MultiTester(parserOptions);

ruleTester.run('no-document-cookie', rule, {
invalid: [
{

// Assignment expression.

code: `
function doSomething(name, value) {
return document.cookie = \`\${name}=\${value}\`;
}
`,
errors: [
{
messageId: 'noDocumentCookie',
type: 'MemberExpression',
},
],
},
{

// Property access expression.

code: `
function doSomething() {
return document.cookie.split(";").length;
}
`,
errors: [
{
messageId: 'noDocumentCookie',
type: 'MemberExpression',
},
],
},
],

valid: [
{

// Named import from frontend-js-web

code: `
import {setCookie, COOKIE_TYPES} from 'frontend-js-web';
function doSomething() {
return setCookie("name", "value", COOKIE_TYPES.NECESSARY);
}
`,
},
{

// Namespaced from Liferay.Util

code: `
function doSomething() {
return Liferay.Util.Cookie.set("name", "value", Liferay.Util.Cookie.TYPES.PERFORMANCE);
}
`,
},
],
});

0 comments on commit 7aa9ea3

Please sign in to comment.