Skip to content

Commit 3104743

Browse files
authoredAug 19, 2019
fix: measure-text security vulnerability (#5)
* chore: Add development setup section to the contributing docs. * fix: measure-text upstream dependency security vulnerability (Prototype pollution).
1 parent 2f1dc21 commit 3104743

File tree

7 files changed

+1004
-825
lines changed

7 files changed

+1004
-825
lines changed
 

‎CONTRIBUTING.md

+44-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,50 @@
22

33
Pull requests of any kind are welcome from the community, and if you're reading this I am really happy that you have decided to contribute. You rock! When contributing to this repository, please first discuss the change you wish to make via a GitHub issue with the owners of this repository before making a change.
44

5-
Please note we have a [Code of Conduct][url-code-of-conduct], please follow it in all your interactions with the project.
5+
Please note we have a [Code of Conduct][url-code-of-conduct], please follow it in all of your interactions with this project.
6+
7+
## Development Setup
8+
9+
**Installation:**
10+
11+
1. Clone the project `git clone git@github.com:matt-d-rat/react-middle-truncate.git`
12+
2. Install npm dependencies `npm install`
13+
14+
**Running the demo app:**
15+
16+
```
17+
npm start
18+
```
19+
20+
***Running Eslint***
21+
22+
```
23+
npm run check
24+
```
25+
26+
**Running the tests:**
27+
28+
_Mocha_
29+
30+
```
31+
npm run test
32+
```
33+
34+
```
35+
npm run test:watch
36+
```
37+
38+
_Karma (in-browser)_
39+
40+
```
41+
npm run test:karma
42+
```
43+
44+
_Test coverage_
45+
46+
```
47+
npm run cover
48+
```
649

750
## Pull Request Process
851

‎config/webpack.base.config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ module.exports = {
2929
fonts: path.join(project.path.src, 'demo/assets/fonts'),
3030
images: path.join(project.path.src, 'demo/assets/images'),
3131
lib: path.join(project.path.src, 'react-middle-truncate'),
32-
scss: path.join(project.path.src, 'demo/assets/scss')
32+
scss: path.join(project.path.src, 'demo/assets/scss'),
33+
utils: path.join(project.path.src, 'utils')
3334
},
3435
extensions: ['.js', '.jsx', '.json', '.scss', '.css']
3536
},

‎package-lock.json

+839-821
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@
126126
"dependencies": {
127127
"classnames": "^2.2.6",
128128
"lodash": "^4.17.10",
129-
"measure-text": "0.0.4",
130129
"normalize.css": "^8.0.0",
131130
"units-css": "^0.4.0"
132131
},

‎src/react-middle-truncate/middle-truncate.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
22
import { debounce, toFinite } from 'lodash';
33
import { findDOMNode } from 'react-dom';
44
import PropTypes from 'prop-types';
5-
import measureText from 'measure-text';
5+
import measureText from 'utils/measure-text';
66
import units from 'units-css';
77

88
const getStartOffset = (start, text) => {

‎src/utils/measure-text/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import measureText from './measure-text';
2+
3+
export default measureText;
4+
export { measureText };
+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2016 Formidable
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
* SOFTWARE.
22+
*/
23+
24+
/**
25+
* Absorb source code for `measure-text` to resolve security vulnerability until
26+
* a new version of `measure-text` is released.
27+
*
28+
* @see https://github.com/matt-d-rat/react-middle-truncate/issues/4
29+
**/
30+
31+
// eslint-env browser
32+
import units from 'units-css';
33+
34+
const DEFAULT_CANVAS = document.createElement('canvas');
35+
const DEFAULT_FONT_WEIGHT = 400;
36+
const DEFAULT_FONT_STYLE = 'normal';
37+
38+
const measureHeight = (size, lineHeight) => {
39+
// If the line-height is unitless,
40+
// multiply it by the font size.
41+
if (!lineHeight.unit) {
42+
return units.parse(
43+
`${size.value * lineHeight.value}${size.unit}`
44+
);
45+
}
46+
47+
// units-css requires the user to provide
48+
// DOM nodes for these units. We don't want
49+
// to pollute our API with that for the time being.
50+
const unitBlacklist = ['%', 'ch', 'cm', 'em', 'ex'];
51+
if (unitBlacklist.indexOf(lineHeight.unit) !== -1) { // eslint-disable-line no-magic-numbers
52+
throw new Error(
53+
`We do not currently support the unit ${lineHeight.unit}
54+
from the provided line-height ${lineHeight.value}.
55+
Unsupported units include ${unitBlacklist.join(', ')}.`
56+
);
57+
}
58+
59+
// Otherwise, the height is equivalent
60+
// to the provided line height.
61+
// Non-px units need conversion.
62+
if (lineHeight.unit === 'px') {
63+
return lineHeight;
64+
}
65+
return units.parse(
66+
units.convert(lineHeight, 'px')
67+
);
68+
};
69+
70+
const measureText = ({
71+
text,
72+
fontFamily,
73+
fontSize,
74+
lineHeight,
75+
fontWeight = DEFAULT_FONT_WEIGHT,
76+
fontStyle = DEFAULT_FONT_STYLE,
77+
canvas = DEFAULT_CANVAS
78+
}) => {
79+
const ctx = canvas.getContext('2d');
80+
ctx.font = `${fontWeight} ${fontStyle} ${fontSize} ${fontFamily}`;
81+
82+
const measure = (line) => {
83+
return {
84+
text: line,
85+
width: units.parse(`${ctx.measureText(line).width}px`),
86+
height: measureHeight(
87+
units.parse(fontSize),
88+
units.parse(lineHeight)
89+
)
90+
};
91+
};
92+
93+
// If multiline, measure the bounds
94+
// of all of the lines combined
95+
if (Array.isArray(text)) {
96+
return text
97+
.map(measure)
98+
.reduce((prev, curr) => {
99+
const width = curr.width.value > prev.width.value
100+
? curr.width : prev.width;
101+
const height = units.parse(
102+
`${prev.height.value + curr.height.value}${curr.height.unit}`
103+
);
104+
const longest = curr.text.length > prev.text.length
105+
? curr.text : prev.text;
106+
return { width, height, text: longest };
107+
});
108+
}
109+
110+
return measure(text);
111+
};
112+
113+
export default measureText;
114+
export { measureText };

0 commit comments

Comments
 (0)
Please sign in to comment.