Security upgrades

This commit is contained in:
2020-07-15 19:54:48 +02:00
parent 59cc6c54cd
commit ad8ed283d2
3164 changed files with 408897 additions and 28 deletions

View File

@@ -0,0 +1,274 @@
import expect from 'expect.js'
import {stripIndent} from 'common-tags'
import {create} from 'jss'
import sinon from 'sinon'
import functionPlugin from '.'
const settings = {createGenerateId: () => rule => `${rule.key}-id`}
describe('jss-plugin-rule-value-function: Function rules', () => {
let jss
beforeEach(() => {
jss = create(settings).use(functionPlugin())
})
describe('basic', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: data => ({
color: data.color,
display: 'block'
})
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should compile correctly', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
display: block;
}
`)
})
})
describe('remove props', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: data => {
if (data.removeAll) {
return null
}
if (data.noDisplay) {
return {color: data.color}
}
return {
color: data.color,
display: 'block'
}
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should compile with color and display', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
display: block;
}
`)
})
it('should compile with color', () => {
sheet.update({color: 'red'})
sheet.update({color: 'red', noDisplay: true})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
`)
})
it('should remove all props', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
display: block;
}
`)
sheet.update({removeAll: true})
expect(sheet.toString()).to.be('.a-id {}')
})
})
describe('fallbacks inside', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: data => ({
color: data.color,
fallbacks: {
color: 'green'
}
})
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should output with fallbacks', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: green;
color: red;
}
`)
})
})
describe('@media with fn values', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
'@media all': {
a: {
color: ({color}) => color
}
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
@media all {
.a-id {
color: red;
}
}
`)
})
})
describe('.addRule() with style rule', () => {
let sheet
beforeEach(() => {
sheet = jss.createStyleSheet(null, {link: true}).attach()
sheet.addRule('a', data => ({
color: data.primary ? 'black' : 'white'
}))
})
afterEach(() => {
sheet.detach()
})
it('should compile correct CSS', () => {
sheet.update({primary: true})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: black;
}
`)
})
it('should return correct .toString()', () => {
sheet.update({primary: false})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: white;
}
`)
})
})
describe('.addRule() with @media', () => {
let sheet
beforeEach(() => {
sheet = jss.createStyleSheet({}, {link: true}).attach()
sheet.addRule('@media screen', {
b: data => ({
color: data.primary ? 'black' : 'white'
})
})
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({primary: true})
expect(sheet.toString()).to.be(stripIndent`
@media screen {
.b-id {
color: black;
}
}
`)
})
})
describe('function value inside', () => {
let sheet
let spy
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: data => ({
color: () => data.color,
background: () => data.background
})
},
{link: true}
)
.attach()
spy = sinon.spy(console, 'warn')
})
afterEach(() => {
sheet.detach()
console.warn.restore()
})
it('should warn when a function value is inside a function rule', () => {
sheet.update({color: 'blue', background: 'green'})
expect(spy.callCount).to.be(1)
expect(
spy.calledWithExactly(
'Warning: [JSS] Function values inside function rules are not supported.'
)
).to.be(true)
})
})
})

View File

@@ -0,0 +1,434 @@
/* eslint-disable no-underscore-dangle */
import {stripIndent} from 'common-tags'
import expect from 'expect.js'
import {create} from 'jss'
import functionPlugin from '.'
const settings = {createGenerateId: () => rule => `${rule.key}-id`}
describe('jss-plugin-rule-value-function: Function values', () => {
let jss
beforeEach(() => {
jss = create(settings).use(functionPlugin())
})
describe('.addRule() with @media with function values', () => {
let sheet
beforeEach(() => {
sheet = jss.createStyleSheet({}, {link: true}).attach()
sheet.addRule('@media screen', {
b: {
color: props => (props.primary ? 'black' : 'white')
}
})
})
afterEach(() => {
sheet.detach()
})
})
describe('.addRule() with function values and attached sheet', () => {
let sheet
beforeEach(() => {
sheet = jss.createStyleSheet(null, {link: true}).attach()
sheet.addRule('a', {color: ({color}) => color})
})
afterEach(() => {
sheet.detach()
})
it('should render an empty rule', () => {
expect(sheet.toString()).to.be(stripIndent`
.a-id {}
`)
})
it('should render rule with updated color', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
`)
})
})
describe('.addRule() with function values for rules from plugins queue', () => {
let sheet
beforeEach(() => {
jss.use({
onProcessRule(rule, ruleSheet) {
const ruleName = 'plugin-rule'
if (rule.key === ruleName) return
ruleSheet.addRule(ruleName, {
color: props => props.color
})
}
})
sheet = jss.createStyleSheet({}, {link: true}).attach()
})
afterEach(() => {
sheet.detach()
})
it('should render color for rule by plugin', () => {
sheet.addRule('rule', {
color: props => props.color
})
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.rule-id {
color: red;
}
.plugin-rule-id {
color: red;
}
`)
})
})
describe('.addRule() with arrays returned from function values', () => {
let sheet
beforeEach(() => {
sheet = jss.createStyleSheet(null, {link: true}).attach()
sheet.addRule('a', {color: ({color}) => color})
})
afterEach(() => {
sheet.detach()
})
it('should render an empty rule', () => {
expect(sheet.toString()).to.be(stripIndent`
.a-id {}
`)
})
it('should return correct CSS from an array with a single value', () => {
sheet.update({color: ['blue']})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: blue;
}
`)
})
it('should return correct CSS from a double array with !important', () => {
sheet.update({color: [['blue'], '!important']})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: blue !important;
}
`)
})
it('should return correct CSS from an array with !important', () => {
sheet.update({color: ['blue', '!important']})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: blue !important;
}
`)
})
it('should return a property value from the CSSOM getPropertyValue function of "green" with important', () => {
sheet.update({color: [['green'], '!important']})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: green !important;
}
`)
})
it('should return a property value from the CSSOM getPropertyValue function of "green"', () => {
sheet.update({color: ['green']})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: green;
}
`)
})
it('should return a correct priority', () => {
sheet.update({color: [['red'], '!important']})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red !important;
}
`)
})
})
describe('sheet.update()', () => {
let sheet
const styles = {
a: {
color: theme => theme.color
},
'@media all': {
b: {
color: theme => theme.color
}
},
'@keyframes a': {
'0%': {
color: theme => theme.color
}
}
}
beforeEach(() => {
sheet = jss.createStyleSheet(styles, {link: true})
})
afterEach(() => {
sheet.detach()
})
describe('.toString()', () => {
it('should return correct .toString() before .update()', () => {
expect(sheet.toString()).to.be(stripIndent`
.a-id {}
@media all {
.b-id { }
}
@keyframes keyframes-a-id {
0% { }
}
`)
})
it('should return correct .toString() after single .update()', () => {
sheet.update({
color: 'green'
})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: green;
}
@media all {
.b-id {
color: green;
}
}
@keyframes keyframes-a-id {
0% {
color: green;
}
}
`)
})
it('should return correct .toString() after double .update()', () => {
sheet.update({
color: 'green'
})
sheet.update({
color: 'yellow'
})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: yellow;
}
@media all {
.b-id {
color: yellow;
}
}
@keyframes keyframes-a-id {
0% {
color: yellow;
}
}
`)
})
it('should update specific rule', () => {
sheet.update({color: 'yellow'})
sheet.update('a', {color: 'green'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: green;
}
@media all {
.b-id {
color: yellow;
}
}
@keyframes keyframes-a-id {
0% {
color: yellow;
}
}
`)
})
it('should remove declarations when value is null', () => {
sheet.update({color: null})
expect(sheet.toString()).to.be(stripIndent`
.a-id {}
@media all {
.b-id { }
}
@keyframes keyframes-a-id {
0% { }
}
`)
})
it('should remove declarations when value is undefined', () => {
sheet.update({color: undefined})
expect(sheet.toString()).to.be(stripIndent`
.a-id {}
@media all {
.b-id { }
}
@keyframes keyframes-a-id {
0% { }
}
`)
})
it('should remove declarations when value is false', () => {
sheet.update({color: false})
expect(sheet.toString()).to.be(stripIndent`
.a-id {}
@media all {
.b-id { }
}
@keyframes keyframes-a-id {
0% { }
}
`)
})
})
describe('sheet.update() after attach', () => {
beforeEach(() => {
sheet = jss.createStyleSheet(
{
a: {
color: theme => theme.color
}
},
{link: true}
)
})
it('should render sheet with updated props after attach', () => {
sheet.attach().update({color: 'green'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: green;
}
`)
})
it('should render updated rule after attach', () => {
sheet.attach().update('a', {color: 'green'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: green;
}
`)
})
})
})
describe('keyframe names', () => {
it('should work with animation-name', () => {
const sheet = jss.createStyleSheet({
'@keyframes animateIn': {},
'@keyframes animateOut': {},
a: {'animation-name': ({name}) => name}
})
sheet.update({name: '$animateIn'})
expect(sheet.toString()).to.be(stripIndent`
@keyframes keyframes-animateIn-id {}
@keyframes keyframes-animateOut-id {}
.a-id {
animation-name: keyframes-animateIn-id;
}
`)
sheet.update({name: '$animateOut'})
expect(sheet.toString()).to.be(stripIndent`
@keyframes keyframes-animateIn-id {}
@keyframes keyframes-animateOut-id {}
.a-id {
animation-name: keyframes-animateOut-id;
}
`)
})
it('should work with animation prop', () => {
const sheet = jss.createStyleSheet({
'@keyframes animateIn': {},
'@keyframes animateOut': {},
a: {animation: ({name}) => `${name} 5s`}
})
sheet.update({name: '$animateIn'})
expect(sheet.toString()).to.be(stripIndent`
@keyframes keyframes-animateIn-id {}
@keyframes keyframes-animateOut-id {}
.a-id {
animation: keyframes-animateIn-id 5s;
}
`)
sheet.update({name: '$animateOut'})
expect(sheet.toString()).to.be(stripIndent`
@keyframes keyframes-animateIn-id {}
@keyframes keyframes-animateOut-id {}
.a-id {
animation: keyframes-animateOut-id 5s;
}
`)
})
})
describe('rule.toJSON()', () => {
it('should handle function values', () => {
const sheet = jss.createStyleSheet({
a: {color: () => 'red'}
})
sheet.update()
expect(sheet.getRule('a').toJSON()).to.eql({color: 'red'})
})
})
})

View File

@@ -0,0 +1,3 @@
import {Plugin} from 'jss'
export default function jssPluginSyntaxRuleValueFunction(): Plugin

View File

@@ -0,0 +1,80 @@
/* @flow */
import warning from 'tiny-warning'
import {
createRule,
type Rule,
type JssStyle,
type RuleOptions,
type UpdateOptions,
type StyleRule,
type StyleSheet
} from 'jss'
// A symbol replacement.
let now = Date.now()
const fnValuesNs = `fnValues${now}`
const fnRuleNs = `fnStyle${++now}`
type StyleRuleWithRuleFunction = StyleRule & {[key: string]: Function}
export default function functionPlugin() {
return {
onCreateRule(name?: string, decl: JssStyle, options: RuleOptions): Rule | null {
if (typeof decl !== 'function') return null
const rule: StyleRuleWithRuleFunction = (createRule(name, {}, options): any)
rule[fnRuleNs] = decl
return rule
},
onProcessStyle(style: JssStyle, rule: Rule): JssStyle {
// We need to extract function values from the declaration, so that we can keep core unaware of them.
// We need to do that only once.
// We don't need to extract functions on each style update, since this can happen only once.
// We don't support function values inside of function rules.
if (fnValuesNs in rule || fnRuleNs in rule) return style
const fnValues = {}
for (const prop in style) {
const value = style[prop]
if (typeof value !== 'function') continue
delete style[prop]
fnValues[prop] = value
}
// $FlowFixMe
rule[fnValuesNs] = fnValues
return style
},
onUpdate(data: Object, rule: Rule, sheet: StyleSheet, options: UpdateOptions) {
const styleRule: StyleRule = (rule: any)
const fnRule = styleRule[fnRuleNs]
// If we have a style function, the entire rule is dynamic and style object
// will be returned from that function.
if (fnRule) {
// Empty object will remove all currently defined props
// in case function rule returns a falsy value.
styleRule.style = fnRule(data) || {}
if (process.env.NODE_ENV === 'development') {
for (const prop in styleRule.style) {
if (typeof styleRule.style[prop] === 'function') {
warning(false, '[JSS] Function values inside function rules are not supported.')
break
}
}
}
}
const fnValues = styleRule[fnValuesNs]
// If we have a fn values map, it is a rule with function values.
if (fnValues) {
for (const prop in fnValues) {
styleRule.prop(prop, fnValues[prop](data), options)
}
}
}
}
}

View File

@@ -0,0 +1,6 @@
import './function-rules.test'
import './function-values.test'
import './plugin-expand.test'
import './plugin-nested.test'
import './plugin-global.test'
import './plugin-compose.test'

View File

@@ -0,0 +1,97 @@
import expect from 'expect.js'
import {stripIndent} from 'common-tags'
import {create} from 'jss'
import pluginCompose from 'jss-plugin-compose'
import pluginFunction from '.'
const settings = {createGenerateId: () => rule => `${rule.key}-id`}
describe('jss-plugin-rule-value-function: plugin-compose', () => {
let jss
beforeEach(() => {
jss = create(settings).use(pluginFunction(), pluginCompose())
})
describe('composing fn value', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: {
color: () => 'red'
},
b: {
color: 'green',
composes: '$a'
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update()
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
.b-id {
color: green;
}
`)
})
it('should have composed class names', () => {
expect(sheet.classes).to.eql({a: 'a-id', b: 'b-id a-id'})
})
})
describe('composing fn rule', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: () => ({
color: 'red'
}),
b: {
color: 'green',
composes: '$a'
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update()
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
.b-id {
color: green;
}
`)
})
it('should have composed class names', () => {
expect(sheet.classes).to.eql({a: 'a-id', b: 'b-id a-id'})
})
})
})

View File

@@ -0,0 +1,111 @@
import expect from 'expect.js'
import {stripIndent} from 'common-tags'
import {create} from 'jss'
import pluginExpand from '../../jss-plugin-expand'
import pluginFunction from '.'
const settings = {createGenerateId: () => rule => `${rule.key}-id`}
describe('jss-plugin-rule-value-function: plugin-expand', () => {
let jss
beforeEach(() => {
jss = create(settings).use(pluginFunction(), pluginExpand())
})
describe('expanding in fn values', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: {
border: props => ({
top: props.border,
bottom: props.border
})
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it.skip('should return correct .toString()', () => {
sheet.update({border: '1px'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
border-top: 1px;
border-bottom: 1px;
}
`)
})
it.skip('should handle when updating multiple times', () => {
sheet.update({border: '1px'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
border-top: 1px;
border-bottom: 1px;
}
`)
sheet.update({border: '5px'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
border-top: 5px;
border-bottom: 5px;
}
`)
})
})
describe('expanding in fn rules', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: props => ({border: {color: props.color}})
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
border-color: red;
}
`)
})
it('should handle updating multiple times', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
border-color: red;
}
`)
sheet.update({color: 'blue'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
border-color: blue;
}
`)
})
})
})

View File

@@ -0,0 +1,89 @@
import expect from 'expect.js'
import {stripIndent} from 'common-tags'
import {create} from 'jss'
import pluginGlobal from 'jss-plugin-global'
import pluginFunction from '.'
const settings = {createGenerateId: () => rule => `${rule.key}-id`}
describe('jss-plugin-rule-value-function: plugin-global', () => {
let jss
beforeEach(() => {
jss = create(settings).use(pluginGlobal(), pluginFunction())
})
describe('fn rule', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
'@global': {
a: data => ({
color: data.color
}),
'@media all': {
a: {
color: 'green'
}
}
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
a {
color: red;
}
@media all {
a {
color: green;
}
}
`)
})
})
describe('fn value', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
'@global': {
a: {
color: data => data.color
}
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
a {
color: red;
}
`)
})
})
})

View File

@@ -0,0 +1,211 @@
import expect from 'expect.js'
import {stripIndent} from 'common-tags'
import {create} from 'jss'
import pluginNested from '../../jss-plugin-nested'
import pluginFunction from '.'
const settings = {createGenerateId: () => rule => `${rule.key}-id`}
describe('jss-plugin-rule-value-function: plugin-nested', () => {
let jss
beforeEach(() => {
jss = create(settings).use(pluginNested(), pluginFunction())
})
describe('@media nested in fn rule', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: data => ({
color: data.color,
'@media all': {
color: 'green'
}
})
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'red'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
@media all {
.a-id {
color: green;
}
}
`)
})
})
describe('@media nested as a fn rule', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: {
color: 'red',
'@media all': data => ({
color: data.color
})
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'green'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
@media all {
.a-id {
color: green;
}
}
`)
})
})
describe('nested selector inside of a fn rule', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: ({color}) => ({
color: 'red',
'& a': {
color
}
})
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'green'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
.a-id a {
color: green;
}
`)
})
})
describe('nested selector as a fn rule', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: {
color: 'red',
'& a': ({color}) => ({
color
})
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update({color: 'green'})
expect(sheet.toString()).to.be(stripIndent`
.a-id {
color: red;
}
.a-id a {
color: green;
}
`)
})
})
describe('deeply nested selector as a fn value', () => {
let sheet
beforeEach(() => {
sheet = jss
.createStyleSheet(
{
a: {
padding: '5px'
},
b: {
background: 'blue'
},
c: {
'&$a': {
'& $b': {
margin: () => '10px'
}
}
}
},
{link: true}
)
.attach()
})
afterEach(() => {
sheet.detach()
})
it('should return correct .toString()', () => {
sheet.update()
expect(sheet.toString()).to.be(stripIndent`
.a-id {
padding: 5px;
}
.b-id {
background: blue;
}
.c-id {}
.c-id.a-id {}
.c-id.a-id .b-id {
margin: 10px;
}
`)
})
})
})