Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@fluent/bundle

Package Overview
Dependencies
Maintainers
4
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fluent/bundle - npm Package Compare versions

Comparing version 0.14.0 to 0.14.1

13

CHANGELOG.md
# Changelog
## @fluent/bundle 0.14.1 (December 20, 2019)
- Fix a big which made placeables which resolved to long strings format as
`{???}`. (#439)
Expressions which resolved to strings over 2500 characters long used to be
considered dangerous. This is no longer the case. Instead, there's a limit
on how many placeable can be resolved during a single call to
`formatPattern`, to protect from high CPU usage in deeply nested patterns.
- Fix a bug which made it impossible to pass a variable called `hasOwnProperty`
to `formatPattern`. (#428)
## @fluent/bundle 0.14.0 (July 30, 2019)

@@ -4,0 +17,0 @@

360

compat.js

@@ -1,2 +0,2 @@

/* @fluent/bundle@0.14.0 */
/* @fluent/bundle@0.14.1 */
(function (global, factory) {

@@ -6,3 +6,3 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :

(global = global || self, factory(global.FluentBundle = {}));
}(this, function (exports) { 'use strict';
}(this, (function (exports) { 'use strict';

@@ -69,3 +69,3 @@ /* global Intl */

constructor() {
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "???";
var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "???";
super(value);

@@ -111,3 +111,3 @@ }

try {
const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts);
var nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts);
return nf.format(this.value);

@@ -148,3 +148,3 @@ } catch (err) {

try {
const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts);
var dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts);
return dtf.format(this.value);

@@ -174,16 +174,31 @@ } catch (err) {

function _objectSpread(target) {
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}

@@ -203,2 +218,6 @@

function _iterableToArrayLimit(arr, i) {
if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
return;
}
var _arr = [];

@@ -238,8 +257,8 @@ var _n = true;

function values(opts) {
const unwrapped = {};
var unwrapped = {};
for (var _i = 0, _Object$entries = Object.entries(opts); _i < _Object$entries.length; _i++) {
const _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
name = _Object$entries$_i[0],
opt = _Object$entries$_i[1];
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
name = _Object$entries$_i[0],
opt = _Object$entries$_i[1];

@@ -253,3 +272,3 @@ unwrapped[name] = opt.valueOf();

function NUMBER(_ref, opts) {
let _ref2 = _slicedToArray(_ref, 1),
var _ref2 = _slicedToArray(_ref, 1),
arg = _ref2[0];

@@ -261,3 +280,3 @@

let value = Number(arg.valueOf());
var value = Number(arg.valueOf());

@@ -271,3 +290,3 @@ if (Number.isNaN(value)) {

function DATETIME(_ref3, opts) {
let _ref4 = _slicedToArray(_ref3, 1),
var _ref4 = _slicedToArray(_ref3, 1),
arg = _ref4[0];

@@ -279,3 +298,3 @@

let value = Number(arg.valueOf());
var value = Number(arg.valueOf());

@@ -290,2 +309,3 @@ if (Number.isNaN(value)) {

var builtins = /*#__PURE__*/Object.freeze({
__proto__: null,
NUMBER: NUMBER,

@@ -295,7 +315,11 @@ DATETIME: DATETIME

const MAX_PLACEABLE_LENGTH = 2500; // Unicode bidi isolation characters.
/* global Intl */
// `formatPattern`. The limit protects against the Billion Laughs and Quadratic
// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx.
const FSI = "\u2068";
const PDI = "\u2069"; // Helper: match a variant key to the given selector.
var MAX_PLACEABLES = 100; // Unicode bidi isolation characters.
var FSI = "\u2068";
var PDI = "\u2069"; // Helper: match a variant key to the given selector.
function match(scope, selector, key) {

@@ -313,3 +337,3 @@ if (key === selector) {

if (selector instanceof FluentNumber && typeof key === "string") {
let category = scope.memoizeIntlObject(Intl.PluralRules, selector.opts).select(selector.value);
var category = scope.memoizeIntlObject(Intl.PluralRules, selector.opts).select(selector.value);

@@ -336,4 +360,4 @@ if (key === category) {

function getArguments(scope, args) {
const positional = [];
const named = {};
var positional = [];
var named = Object.create(null);
var _iteratorNormalCompletion = true;

@@ -345,3 +369,3 @@ var _didIteratorError = false;

for (var _iterator = args[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
const arg = _step.value;
var arg = _step.value;

@@ -369,3 +393,6 @@ if (arg.type === "narg") {

return [positional, named];
return {
positional,
named
};
} // Resolve an expression to a Fluent type.

@@ -406,13 +433,21 @@

function VariableReference(scope, _ref) {
let name = _ref.name;
var name = _ref.name;
var arg;
if (!scope.args || !scope.args.hasOwnProperty(name)) {
if (scope.insideTermReference === false) {
scope.reportError(new ReferenceError("Unknown variable: $".concat(name)));
if (scope.params) {
// We're inside a TermReference. It's OK to reference undefined parameters.
if (Object.prototype.hasOwnProperty.call(scope.params, name)) {
arg = scope.params[name];
} else {
return new FluentNone("$".concat(name));
}
} else if (scope.args && Object.prototype.hasOwnProperty.call(scope.args, name)) {
// We're in the top-level Pattern or inside a MessageReference. Missing
// variables references produce ReferenceErrors.
arg = scope.args[name];
} else {
scope.reportError(new ReferenceError("Unknown variable: $".concat(name)));
return new FluentNone("$".concat(name));
}
} // Return early if the argument already is an instance of FluentType.
const arg = scope.args[name]; // Return early if the argument already is an instance of FluentType.

@@ -444,6 +479,6 @@ if (arg instanceof FluentType) {

function MessageReference(scope, _ref2) {
let name = _ref2.name,
var name = _ref2.name,
attr = _ref2.attr;
const message = scope.bundle._messages.get(name);
var message = scope.bundle._messages.get(name);

@@ -456,3 +491,3 @@ if (!message) {

if (attr) {
const attribute = message.attributes[attr];
var attribute = message.attributes[attr];

@@ -477,8 +512,8 @@ if (attribute) {

function TermReference(scope, _ref3) {
let name = _ref3.name,
var name = _ref3.name,
attr = _ref3.attr,
args = _ref3.args;
const id = "-".concat(name);
var id = "-".concat(name);
const term = scope.bundle._terms.get(id);
var term = scope.bundle._terms.get(id);

@@ -488,16 +523,15 @@ if (!term) {

return new FluentNone(id);
} // Every TermReference has its own variables.
}
if (attr) {
var attribute = term.attributes[attr];
const _getArguments = getArguments(scope, args),
_getArguments2 = _slicedToArray(_getArguments, 2),
params = _getArguments2[1];
if (attribute) {
// Every TermReference has its own variables.
scope.params = getArguments(scope, args).named;
const local = scope.cloneForTermReference(params);
var _resolved = resolvePattern(scope, attribute);
if (attr) {
const attribute = term.attributes[attr];
if (attribute) {
return resolvePattern(local, attribute);
scope.params = null;
return _resolved;
}

@@ -509,3 +543,6 @@

return resolvePattern(local, term.value);
scope.params = getArguments(scope, args).named;
var resolved = resolvePattern(scope, term.value);
scope.params = null;
return resolved;
} // Resolve a call to a Function with positional and key-value arguments.

@@ -515,7 +552,7 @@

function FunctionReference(scope, _ref4) {
let name = _ref4.name,
var name = _ref4.name,
args = _ref4.args;
// Some functions are built-in. Others may be provided by the runtime via
// the `FluentBundle` constructor.
const func = scope.bundle._functions[name] || builtins[name];
var func = scope.bundle._functions[name] || builtins[name];

@@ -533,3 +570,4 @@ if (!func) {

try {
return func(...getArguments(scope, args));
var resolved = getArguments(scope, args);
return func(resolved.positional, resolved.named);
} catch (err) {

@@ -543,6 +581,6 @@ scope.reportError(err);

function SelectExpression(scope, _ref5) {
let selector = _ref5.selector,
var selector = _ref5.selector,
variants = _ref5.variants,
star = _ref5.star;
let sel = resolveExpression(scope, selector);
var sel = resolveExpression(scope, selector);

@@ -560,4 +598,4 @@ if (sel instanceof FluentNone) {

for (var _iterator2 = variants[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
const variant = _step2.value;
const key = resolveExpression(scope, variant.key);
var variant = _step2.value;
var key = resolveExpression(scope, variant.key);

@@ -595,6 +633,6 @@ if (match(scope, sel, key)) {

scope.dirty.add(ptn);
const result = []; // Wrap interpolations with Directional Isolate Formatting characters
var result = []; // Wrap interpolations with Directional Isolate Formatting characters
// only when the pattern has more than one element.
const useIsolating = scope.bundle._useIsolating && ptn.length > 1;
var useIsolating = scope.bundle._useIsolating && ptn.length > 1;
var _iteratorNormalCompletion3 = true;

@@ -606,3 +644,3 @@ var _didIteratorError3 = false;

for (var _iterator3 = ptn[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
const elem = _step3.value;
var elem = _step3.value;

@@ -614,9 +652,5 @@ if (typeof elem === "string") {

const part = resolveExpression(scope, elem).toString(scope);
scope.placeables++;
if (useIsolating) {
result.push(FSI);
}
if (part.length > MAX_PLACEABLE_LENGTH) {
if (scope.placeables > MAX_PLACEABLES) {
scope.dirty.delete(ptn); // This is a fatal error which causes the resolver to instantly bail out

@@ -627,7 +661,11 @@ // on this pattern. The length check protects against excessive memory

throw new RangeError("Too many characters in placeable " + "(".concat(part.length, ", max allowed is ").concat(MAX_PLACEABLE_LENGTH, ")"));
throw new RangeError("Too many placeables expanded: ".concat(scope.placeables, ", ") + "max allowed is ".concat(MAX_PLACEABLES));
}
result.push(part);
if (useIsolating) {
result.push(FSI);
}
result.push(resolveExpression(scope, elem).toString(scope));
if (useIsolating) {

@@ -668,5 +706,2 @@ result.push(PDI);

constructor(bundle, errors, args) {
let insideTermReference = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
let dirty = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : new WeakSet();
/** The bundle for which the given resolution is happening. */

@@ -680,13 +715,13 @@ this.bundle = bundle;

this.args = args;
/** Term references require different variable lookup logic. */
this.insideTermReference = insideTermReference;
/** The Set of patterns already encountered during this resolution.
* Used to detect and prevent cyclic resolutions. */
this.dirty = dirty;
}
this.dirty = new WeakSet();
/** A dict of parameters passed to a TermReference. */
cloneForTermReference(args) {
return new Scope(this.bundle, this.errors, args, true, this.dirty);
this.params = null;
/** The running count of placeables resolved so far. Used to detect the
* Billion Laughs and Quadratic Blowup attacks. */
this.placeables = 0;
}

@@ -703,3 +738,3 @@

memoizeIntlObject(ctor, opts) {
let cache = this.bundle._intls.get(ctor);
var cache = this.bundle._intls.get(ctor);

@@ -712,3 +747,3 @@ if (!cache) {

let id = JSON.stringify(opts);
var id = JSON.stringify(opts);

@@ -764,3 +799,3 @@ if (!cache[id]) {

constructor(locales) {
let _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref$functions = _ref.functions,

@@ -834,10 +869,10 @@ functions = _ref$functions === void 0 ? {} : _ref$functions,

addResource(res) {
let _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref2$allowOverrides = _ref2.allowOverrides,
allowOverrides = _ref2$allowOverrides === void 0 ? false : _ref2$allowOverrides;
const errors = [];
var errors = [];
for (let i = 0; i < res.body.length; i++) {
let entry = res.body[i];
for (var i = 0; i < res.body.length; i++) {
var entry = res.body[i];

@@ -907,6 +942,6 @@ if (entry.id.startsWith("-")) {

let scope = new Scope(this, errors, args);
var scope = new Scope(this, errors, args);
try {
let value = resolveComplexPattern(scope, pattern);
var value = resolveComplexPattern(scope, pattern);
return value.toString(scope);

@@ -929,11 +964,11 @@ } catch (err) {

const RE_MESSAGE_START = /^(-?[a-zA-Z][\w-]*) *= */mg; // Both Attributes and Variants are parsed in while loops. These regexes are
var RE_MESSAGE_START = /^(-?[a-zA-Z][\w-]*) *= */mg; // Both Attributes and Variants are parsed in while loops. These regexes are
// used to break out of them.
const RE_ATTRIBUTE_START = /\.([a-zA-Z][\w-]*) *= */y;
const RE_VARIANT_START = /\*?\[/y;
const RE_NUMBER_LITERAL = /(-?[0-9]+(?:\.([0-9]+))?)/y;
const RE_IDENTIFIER = /([a-zA-Z][\w-]*)/y;
const RE_REFERENCE = /([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y;
const RE_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/; // A "run" is a sequence of text or string literal characters which don't
var RE_ATTRIBUTE_START = /\.([a-zA-Z][\w-]*) *= */y;
var RE_VARIANT_START = /\*?\[/y;
var RE_NUMBER_LITERAL = /(-?[0-9]+(?:\.([0-9]+))?)/y;
var RE_IDENTIFIER = /([a-zA-Z][\w-]*)/y;
var RE_REFERENCE = /([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y;
var RE_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/; // A "run" is a sequence of text or string literal characters which don't
// require any special handling. For TextElements such special characters are: {

@@ -945,29 +980,26 @@ // (starts a placeable), and line breaks which require additional logic to check

const RE_TEXT_RUN = /([^{}\n\r]+)/y;
const RE_STRING_RUN = /([^\\"\n\r]*)/y; // Escape sequences.
var RE_TEXT_RUN = /([^{}\n\r]+)/y;
var RE_STRING_RUN = /([^\\"\n\r]*)/y; // Escape sequences.
const RE_STRING_ESCAPE = /\\([\\"])/y;
const RE_UNICODE_ESCAPE = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y; // Used for trimming TextElements and indents.
var RE_STRING_ESCAPE = /\\([\\"])/y;
var RE_UNICODE_ESCAPE = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y; // Used for trimming TextElements and indents.
const RE_LEADING_NEWLINES = /^\n+/;
const RE_TRAILING_SPACES = / +$/; // Used in makeIndent to strip spaces from blank lines and normalize CRLF to LF.
var RE_LEADING_NEWLINES = /^\n+/;
var RE_TRAILING_SPACES = / +$/; // Used in makeIndent to strip spaces from blank lines and normalize CRLF to LF.
const RE_BLANK_LINES = / *\r?\n/g; // Used in makeIndent to measure the indentation.
var RE_BLANK_LINES = / *\r?\n/g; // Used in makeIndent to measure the indentation.
const RE_INDENT = /( *)$/; // Common tokens.
var RE_INDENT = /( *)$/; // Common tokens.
const TOKEN_BRACE_OPEN = /{\s*/y;
const TOKEN_BRACE_CLOSE = /\s*}/y;
const TOKEN_BRACKET_OPEN = /\[\s*/y;
const TOKEN_BRACKET_CLOSE = /\s*] */y;
const TOKEN_PAREN_OPEN = /\s*\(\s*/y;
const TOKEN_ARROW = /\s*->\s*/y;
const TOKEN_COLON = /\s*:\s*/y; // Note the optional comma. As a deviation from the Fluent EBNF, the parser
var TOKEN_BRACE_OPEN = /{\s*/y;
var TOKEN_BRACE_CLOSE = /\s*}/y;
var TOKEN_BRACKET_OPEN = /\[\s*/y;
var TOKEN_BRACKET_CLOSE = /\s*] */y;
var TOKEN_PAREN_OPEN = /\s*\(\s*/y;
var TOKEN_ARROW = /\s*->\s*/y;
var TOKEN_COLON = /\s*:\s*/y; // Note the optional comma. As a deviation from the Fluent EBNF, the parser
// doesn't enforce commas between call arguments.
const TOKEN_COMMA = /\s*,?\s*/y;
const TOKEN_BLANK = /\s+/y; // Maximum number of placeables in a single Pattern to protect against Quadratic
// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx.
const MAX_PLACEABLES = 100;
var TOKEN_COMMA = /\s*,?\s*/y;
var TOKEN_BLANK = /\s+/y;
/**

@@ -984,8 +1016,8 @@ * Fluent Resource is a structure storing parsed localization entries.

RE_MESSAGE_START.lastIndex = 0;
let resource = [];
let cursor = 0; // Iterate over the beginnings of messages and terms to efficiently skip
var resource = [];
var cursor = 0; // Iterate over the beginnings of messages and terms to efficiently skip
// comments and recover from errors.
while (true) {
let next = RE_MESSAGE_START.exec(source);
var next = RE_MESSAGE_START.exec(source);

@@ -1063,3 +1095,3 @@ if (next === null) {

re.lastIndex = cursor;
let result = re.exec(source);
var result = re.exec(source);

@@ -1080,4 +1112,4 @@ if (result === null) {

function parseMessage(id) {
let value = parsePattern();
let attributes = parseAttributes();
var value = parsePattern();
var attributes = parseAttributes();

@@ -1096,7 +1128,7 @@ if (value === null && Object.keys(attributes).length === 0) {

function parseAttributes() {
let attrs = Object.create(null);
var attrs = Object.create(null);
while (test(RE_ATTRIBUTE_START)) {
let name = match1(RE_ATTRIBUTE_START);
let value = parsePattern();
var name = match1(RE_ATTRIBUTE_START);
var value = parsePattern();

@@ -1127,3 +1159,3 @@ if (value === null) {

let indent = parseIndent();
var indent = parseIndent();

@@ -1154,5 +1186,4 @@ if (indent) {

function parsePatternElements() {
let elements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let commonIndent = arguments.length > 1 ? arguments[1] : undefined;
let placeableCount = 0;
var elements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var commonIndent = arguments.length > 1 ? arguments[1] : undefined;

@@ -1166,6 +1197,2 @@ while (true) {

if (source[cursor] === "{") {
if (++placeableCount > MAX_PLACEABLES) {
throw new FluentError("Too many placeables");
}
elements.push(parsePlaceable());

@@ -1179,3 +1206,3 @@ continue;

let indent = parseIndent();
var indent = parseIndent();

@@ -1191,3 +1218,3 @@ if (indent) {

let lastIndex = elements.length - 1; // Trim the trailing spaces in the last element if it's a TextElement.
var lastIndex = elements.length - 1; // Trim the trailing spaces in the last element if it's a TextElement.

@@ -1198,3 +1225,3 @@ if (typeof elements[lastIndex] === "string") {

let baked = [];
var baked = [];
var _iteratorNormalCompletion = true;

@@ -1206,3 +1233,3 @@ var _didIteratorError = false;

for (var _iterator = elements[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
let element = _step.value;
var element = _step.value;

@@ -1238,3 +1265,3 @@ if (element.type === "indent") {

consumeToken(TOKEN_BRACE_OPEN, FluentError);
let selector = parseInlineExpression();
var selector = parseInlineExpression();

@@ -1246,5 +1273,5 @@ if (consumeToken(TOKEN_BRACE_CLOSE)) {

if (consumeToken(TOKEN_ARROW)) {
let variants = parseVariants();
var variants = parseVariants();
consumeToken(TOKEN_BRACE_CLOSE, FluentError);
return _objectSpread({
return _objectSpread2({
type: "select",

@@ -1265,3 +1292,3 @@ selector

if (test(RE_REFERENCE)) {
let _match = match(RE_REFERENCE),
var _match = match(RE_REFERENCE),
_match2 = _slicedToArray(_match, 4),

@@ -1281,3 +1308,3 @@ sigil = _match2[1],

if (consumeToken(TOKEN_PAREN_OPEN)) {
let args = parseArguments();
var args = parseArguments();

@@ -1326,3 +1353,3 @@ if (sigil === "-") {

function parseArguments() {
let args = [];
var args = [];

@@ -1348,3 +1375,3 @@ while (true) {

function parseArgument() {
let expr = parseInlineExpression();
var expr = parseInlineExpression();

@@ -1369,5 +1396,5 @@ if (expr.type !== "mesg") {

function parseVariants() {
let variants = [];
let count = 0;
let star;
var variants = [];
var count = 0;
var star;

@@ -1379,4 +1406,4 @@ while (test(RE_VARIANT_START)) {

let key = parseVariantKey();
let value = parsePattern();
var key = parseVariantKey();
var value = parsePattern();

@@ -1409,3 +1436,3 @@ if (value === null) {

consumeToken(TOKEN_BRACKET_OPEN, FluentError);
let key = test(RE_NUMBER_LITERAL) ? parseNumberLiteral() : {
var key = test(RE_NUMBER_LITERAL) ? parseNumberLiteral() : {
type: "str",

@@ -1431,3 +1458,3 @@ value: match1(RE_IDENTIFIER)

function parseNumberLiteral() {
let _match3 = match(RE_NUMBER_LITERAL),
var _match3 = match(RE_NUMBER_LITERAL),
_match4 = _slicedToArray(_match3, 3),

@@ -1438,3 +1465,3 @@ value = _match4[1],

let precision = fraction.length;
var precision = fraction.length;
return {

@@ -1449,3 +1476,3 @@ type: "num",

consumeChar("\"", FluentError);
let value = "";
var value = "";

@@ -1479,3 +1506,3 @@ while (true) {

if (test(RE_UNICODE_ESCAPE)) {
let _match5 = match(RE_UNICODE_ESCAPE),
var _match5 = match(RE_UNICODE_ESCAPE),
_match6 = _slicedToArray(_match5, 3),

@@ -1485,3 +1512,3 @@ codepoint4 = _match6[1],

let codepoint = parseInt(codepoint4 || codepoint6, 16);
var codepoint = parseInt(codepoint4 || codepoint6, 16);
return codepoint <= 0xD7FF || 0xE000 <= codepoint // It's a Unicode scalar value.

@@ -1499,3 +1526,3 @@ ? String.fromCodePoint(codepoint) // Lonely surrogates can cause trouble when the parsing result is

function parseIndent() {
let start = cursor;
var start = cursor;
consumeToken(TOKEN_BLANK); // Check the first non-blank character after the indent.

@@ -1540,4 +1567,4 @@

function makeIndent(blank) {
let value = blank.replace(RE_BLANK_LINES, "\n");
let length = RE_INDENT.exec(blank)[1].length;
var value = blank.replace(RE_BLANK_LINES, "\n");
var length = RE_INDENT.exec(blank)[1].length;
return {

@@ -1553,11 +1580,2 @@ type: "indent",

/**
* @module fluent
* @overview
*
* `fluent` is a JavaScript implementation of Project Fluent, a localization
* framework designed to unleash the expressive power of the natural language.
*
*/
exports.FluentBundle = FluentBundle;

@@ -1572,2 +1590,2 @@ exports.FluentDateTime = FluentDateTime;

}));
})));

@@ -1,2 +0,2 @@

/* @fluent/bundle@0.14.0 */
/* @fluent/bundle@0.14.1 */
(function (global, factory) {

@@ -6,3 +6,3 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :

(global = global || self, factory(global.FluentBundle = {}));
}(this, function (exports) { 'use strict';
}(this, (function (exports) { 'use strict';

@@ -195,2 +195,3 @@ /* global Intl */

var builtins = /*#__PURE__*/Object.freeze({
__proto__: null,
NUMBER: NUMBER,

@@ -202,4 +203,6 @@ DATETIME: DATETIME

// Prevent expansion of too long placeables.
const MAX_PLACEABLE_LENGTH = 2500;
// The maximum number of placeables which can be expanded in a single call to
// `formatPattern`. The limit protects against the Billion Laughs and Quadratic
// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx.
const MAX_PLACEABLES = 100;

@@ -250,3 +253,3 @@ // Unicode bidi isolation characters.

const positional = [];
const named = {};
const named = Object.create(null);

@@ -261,3 +264,3 @@ for (const arg of args) {

return [positional, named];
return {positional, named};
}

@@ -291,11 +294,22 @@

function VariableReference(scope, {name}) {
if (!scope.args || !scope.args.hasOwnProperty(name)) {
if (scope.insideTermReference === false) {
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
let arg;
if (scope.params) {
// We're inside a TermReference. It's OK to reference undefined parameters.
if (Object.prototype.hasOwnProperty.call(scope.params, name)) {
arg = scope.params[name];
} else {
return new FluentNone(`$${name}`);
}
} else if (
scope.args
&& Object.prototype.hasOwnProperty.call(scope.args, name)
) {
// We're in the top-level Pattern or inside a MessageReference. Missing
// variables references produce ReferenceErrors.
arg = scope.args[name];
} else {
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
return new FluentNone(`$${name}`);
}
const arg = scope.args[name];
// Return early if the argument already is an instance of FluentType.

@@ -358,10 +372,10 @@ if (arg instanceof FluentType) {

// Every TermReference has its own variables.
const [, params] = getArguments(scope, args);
const local = scope.cloneForTermReference(params);
if (attr) {
const attribute = term.attributes[attr];
if (attribute) {
return resolvePattern(local, attribute);
// Every TermReference has its own variables.
scope.params = getArguments(scope, args).named;
const resolved = resolvePattern(scope, attribute);
scope.params = null;
return resolved;
}

@@ -372,3 +386,6 @@ scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`));

return resolvePattern(local, term.value);
scope.params = getArguments(scope, args).named;
const resolved = resolvePattern(scope, term.value);
scope.params = null;
return resolved;
}

@@ -392,3 +409,4 @@

try {
return func(...getArguments(scope, args));
let resolved = getArguments(scope, args);
return func(resolved.positional, resolved.named);
} catch (err) {

@@ -439,9 +457,4 @@ scope.reportError(err);

const part = resolveExpression(scope, elem).toString(scope);
if (useIsolating) {
result.push(FSI);
}
if (part.length > MAX_PLACEABLE_LENGTH) {
scope.placeables++;
if (scope.placeables > MAX_PLACEABLES) {
scope.dirty.delete(ptn);

@@ -453,9 +466,13 @@ // This is a fatal error which causes the resolver to instantly bail out

throw new RangeError(
"Too many characters in placeable " +
`(${part.length}, max allowed is ${MAX_PLACEABLE_LENGTH})`
`Too many placeables expanded: ${scope.placeables}, ` +
`max allowed is ${MAX_PLACEABLES}`
);
}
result.push(part);
if (useIsolating) {
result.push(FSI);
}
result.push(resolveExpression(scope, elem).toString(scope));
if (useIsolating) {

@@ -482,9 +499,3 @@ result.push(PDI);

class Scope {
constructor(
bundle,
errors,
args,
insideTermReference = false,
dirty = new WeakSet()
) {
constructor(bundle, errors, args) {
/** The bundle for which the given resolution is happening. */

@@ -497,13 +508,12 @@ this.bundle = bundle;

/** Term references require different variable lookup logic. */
this.insideTermReference = insideTermReference;
/** The Set of patterns already encountered during this resolution.
* Used to detect and prevent cyclic resolutions. */
this.dirty = dirty;
this.dirty = new WeakSet();
/** A dict of parameters passed to a TermReference. */
this.params = null;
/** The running count of placeables resolved so far. Used to detect the
* Billion Laughs and Quadratic Blowup attacks. */
this.placeables = 0;
}
cloneForTermReference(args) {
return new Scope(this.bundle, this.errors, args, true, this.dirty);
}
reportError(error) {

@@ -762,6 +772,2 @@ if (!this.errors) {

// Maximum number of placeables in a single Pattern to protect against Quadratic
// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx.
const MAX_PLACEABLES = 100;
/**

@@ -931,4 +937,2 @@ * Fluent Resource is a structure storing parsed localization entries.

function parsePatternElements(elements = [], commonIndent) {
let placeableCount = 0;
while (true) {

@@ -941,5 +945,2 @@ if (test(RE_TEXT_RUN)) {

if (source[cursor] === "{") {
if (++placeableCount > MAX_PLACEABLES) {
throw new FluentError("Too many placeables");
}
elements.push(parsePlaceable());

@@ -1215,11 +1216,2 @@ continue;

/**
* @module fluent
* @overview
*
* `fluent` is a JavaScript implementation of Project Fluent, a localization
* framework designed to unleash the expressive power of the natural language.
*
*/
exports.FluentBundle = FluentBundle;

@@ -1234,2 +1226,2 @@ exports.FluentDateTime = FluentDateTime;

}));
})));
{
"name": "@fluent/bundle",
"description": "Localization library for expressive translations.",
"version": "0.14.0",
"homepage": "http://projectfluent.org",
"version": "0.14.1",
"homepage": "https://projectfluent.org",
"author": "Mozilla <l10n-drivers@mozilla.org>",

@@ -7,0 +7,0 @@ "license": "Apache-2.0",

@@ -43,3 +43,3 @@ # @fluent/bundle

The API reference is available at http://projectfluent.org/fluent.js/fluent.
The API reference is available at https://projectfluent.org/fluent.js/bundle.

@@ -82,3 +82,3 @@

[env preset]: https://babeljs.io/docs/plugins/preset-env/
[projectfluent.org]: http://projectfluent.org
[FTL]: http://projectfluent.org/fluent/guide/
[projectfluent.org]: https://projectfluent.org
[FTL]: https://projectfluent.org/fluent/guide/

@@ -32,4 +32,6 @@ /* global Intl */

// Prevent expansion of too long placeables.
const MAX_PLACEABLE_LENGTH = 2500;
// The maximum number of placeables which can be expanded in a single call to
// `formatPattern`. The limit protects against the Billion Laughs and Quadratic
// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx.
const MAX_PLACEABLES = 100;

@@ -80,3 +82,3 @@ // Unicode bidi isolation characters.

const positional = [];
const named = {};
const named = Object.create(null);

@@ -91,3 +93,3 @@ for (const arg of args) {

return [positional, named];
return {positional, named};
}

@@ -121,11 +123,22 @@

function VariableReference(scope, {name}) {
if (!scope.args || !scope.args.hasOwnProperty(name)) {
if (scope.insideTermReference === false) {
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
let arg;
if (scope.params) {
// We're inside a TermReference. It's OK to reference undefined parameters.
if (Object.prototype.hasOwnProperty.call(scope.params, name)) {
arg = scope.params[name];
} else {
return new FluentNone(`$${name}`);
}
} else if (
scope.args
&& Object.prototype.hasOwnProperty.call(scope.args, name)
) {
// We're in the top-level Pattern or inside a MessageReference. Missing
// variables references produce ReferenceErrors.
arg = scope.args[name];
} else {
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
return new FluentNone(`$${name}`);
}
const arg = scope.args[name];
// Return early if the argument already is an instance of FluentType.

@@ -188,10 +201,10 @@ if (arg instanceof FluentType) {

// Every TermReference has its own variables.
const [, params] = getArguments(scope, args);
const local = scope.cloneForTermReference(params);
if (attr) {
const attribute = term.attributes[attr];
if (attribute) {
return resolvePattern(local, attribute);
// Every TermReference has its own variables.
scope.params = getArguments(scope, args).named;
const resolved = resolvePattern(scope, attribute);
scope.params = null;
return resolved;
}

@@ -202,3 +215,6 @@ scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`));

return resolvePattern(local, term.value);
scope.params = getArguments(scope, args).named;
const resolved = resolvePattern(scope, term.value);
scope.params = null;
return resolved;
}

@@ -222,3 +238,4 @@

try {
return func(...getArguments(scope, args));
let resolved = getArguments(scope, args);
return func(resolved.positional, resolved.named);
} catch (err) {

@@ -269,9 +286,4 @@ scope.reportError(err);

const part = resolveExpression(scope, elem).toString(scope);
if (useIsolating) {
result.push(FSI);
}
if (part.length > MAX_PLACEABLE_LENGTH) {
scope.placeables++;
if (scope.placeables > MAX_PLACEABLES) {
scope.dirty.delete(ptn);

@@ -283,9 +295,13 @@ // This is a fatal error which causes the resolver to instantly bail out

throw new RangeError(
"Too many characters in placeable " +
`(${part.length}, max allowed is ${MAX_PLACEABLE_LENGTH})`
`Too many placeables expanded: ${scope.placeables}, ` +
`max allowed is ${MAX_PLACEABLES}`
);
}
result.push(part);
if (useIsolating) {
result.push(FSI);
}
result.push(resolveExpression(scope, elem).toString(scope));
if (useIsolating) {

@@ -292,0 +308,0 @@ result.push(PDI);

@@ -51,6 +51,2 @@ import FluentError from "./error.js";

// Maximum number of placeables in a single Pattern to protect against Quadratic
// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx.
const MAX_PLACEABLES = 100;
/**

@@ -220,4 +216,2 @@ * Fluent Resource is a structure storing parsed localization entries.

function parsePatternElements(elements = [], commonIndent) {
let placeableCount = 0;
while (true) {

@@ -230,5 +224,2 @@ if (test(RE_TEXT_RUN)) {

if (source[cursor] === "{") {
if (++placeableCount > MAX_PLACEABLES) {
throw new FluentError("Too many placeables");
}
elements.push(parsePlaceable());

@@ -235,0 +226,0 @@ continue;

export default class Scope {
constructor(
bundle,
errors,
args,
insideTermReference = false,
dirty = new WeakSet()
) {
constructor(bundle, errors, args) {
/** The bundle for which the given resolution is happening. */

@@ -16,13 +10,12 @@ this.bundle = bundle;

/** Term references require different variable lookup logic. */
this.insideTermReference = insideTermReference;
/** The Set of patterns already encountered during this resolution.
* Used to detect and prevent cyclic resolutions. */
this.dirty = dirty;
this.dirty = new WeakSet();
/** A dict of parameters passed to a TermReference. */
this.params = null;
/** The running count of placeables resolved so far. Used to detect the
* Billion Laughs and Quadratic Blowup attacks. */
this.placeables = 0;
}
cloneForTermReference(args) {
return new Scope(this.bundle, this.errors, args, true, this.dirty);
}
reportError(error) {

@@ -29,0 +22,0 @@ if (!this.errors) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc