mirror of
https://github.com/hanzoai/postcss.git
synced 2026-09-24 17:01:56 +00:00
122 lines
3.9 KiB
JavaScript
122 lines
3.9 KiB
JavaScript
// Copyright 2026 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
let { FuzzedDataProvider } = require('@jazzer.js/core')
|
|
|
|
let postcss = require('../../lib/postcss')
|
|
|
|
module.exports.fuzz = function (data) {
|
|
let provider = new FuzzedDataProvider(data)
|
|
|
|
// The CSS input itself is randomized: every byte the fuzzer produces (or
|
|
// mutates from the seed corpus) flows directly into `cssString` via
|
|
// consumeRemainingAsString(). The option flags below are read from the
|
|
// *back* of the buffer (jazzer.js consumes integrals/booleans from the
|
|
// tail), so seed CSS files from postcss-parser-tests are fed into the
|
|
// parser nearly verbatim, with only their last few bytes nibbled off as
|
|
// option control.
|
|
let useMap = provider.consumeBoolean()
|
|
let useFrom = provider.consumeBoolean()
|
|
let useProcessor = provider.consumeBoolean()
|
|
let splitMode = provider.consumeIntegralInRange(0, 2)
|
|
let cssString = provider.consumeRemainingAsString()
|
|
|
|
let parseOptions = {}
|
|
if (useFrom) parseOptions.from = 'fuzz.css'
|
|
if (useMap) parseOptions.map = { annotation: false, inline: false }
|
|
|
|
let root
|
|
try {
|
|
root = postcss.parse(cssString, parseOptions)
|
|
} catch (e) {
|
|
if (e instanceof postcss.CssSyntaxError) return
|
|
throw e
|
|
}
|
|
|
|
// Walk the AST and exercise common node accessors. This also stresses
|
|
// raws/source bookkeeping for any node returned by the parser.
|
|
try {
|
|
root.walk(node => {
|
|
void node.type
|
|
void node.toString()
|
|
if (typeof node.error === 'function') {
|
|
// Generating an error message touches input/source-map machinery.
|
|
node.error('fuzz').message
|
|
}
|
|
})
|
|
} catch (e) {
|
|
if (!isExpected(e, postcss)) throw e
|
|
}
|
|
|
|
// Round-trip via stringify and re-parse. Output should itself be parseable.
|
|
let serialized
|
|
try {
|
|
serialized = root.toString()
|
|
} catch (e) {
|
|
if (!isExpected(e, postcss)) throw e
|
|
return
|
|
}
|
|
|
|
try {
|
|
postcss.parse(serialized)
|
|
} catch (e) {
|
|
if (!(e instanceof postcss.CssSyntaxError)) throw e
|
|
}
|
|
|
|
// Exercise the JSON serialization round-trip.
|
|
try {
|
|
let json = root.toJSON()
|
|
postcss.fromJSON(json)
|
|
} catch (e) {
|
|
if (!isExpected(e)) throw e
|
|
}
|
|
|
|
// Exercise the main public entry point: postcss().process(). This drives
|
|
// the LazyResult / NoWorkResult pipeline that real plugin chains use.
|
|
if (useProcessor) {
|
|
try {
|
|
let result = postcss().process(cssString, parseOptions)
|
|
void result.css
|
|
void result.warnings()
|
|
} catch (e) {
|
|
if (!isExpected(e)) throw e
|
|
}
|
|
}
|
|
|
|
// Exercise the list helpers, which have their own quoting/escape logic.
|
|
try {
|
|
if (splitMode === 0) {
|
|
postcss.list.comma(cssString)
|
|
} else if (splitMode === 1) {
|
|
postcss.list.space(cssString)
|
|
} else {
|
|
postcss.list.split(cssString, [',', ' '], false)
|
|
}
|
|
} catch (e) {
|
|
if (!isExpected(e)) throw e
|
|
}
|
|
}
|
|
|
|
function isExpected(error) {
|
|
if (error instanceof postcss.CssSyntaxError) return true
|
|
if (!error || typeof error.message !== 'string') return false
|
|
// Some legitimate inputs reach known-shaped TypeErrors during stringify or
|
|
// walk because the CSS allows constructs whose textual form is ambiguous.
|
|
// Suppress only those well-defined cases so real bugs still surface.
|
|
let benign = ['Unknown node type', 'Unknown word']
|
|
return benign.some(msg => error.message.indexOf(msg) !== -1)
|
|
}
|