New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

json-alexander

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-alexander - npm Package Compare versions

Comparing version 0.0.4 to 0.1.0

4

package.json
{
"name": "json-alexander",
"version": "0.0.4",
"description": "Serenity Now! Safely parse JSON",
"version": "0.1.0",
"description": "Serenity Now! Forgiving JSON parser",
"main": "src/index.js",

@@ -6,0 +6,0 @@ "scripts": {

@@ -1,6 +0,6 @@

# Friendly JSON Parse
# Forgiving JSON Parser
<img align="right" src="https://user-images.githubusercontent.com/532272/64802133-d3d2b180-d53e-11e9-8182-101a1b927e29.jpg">
Serenity now! Safe `JSON.parse`
Serenity now! A forgiving JSON parser 🙏

@@ -25,5 +25,9 @@ ```js

// -> {"unbalanced": "object" }
/* Javascript objects missing quotes */
parseJSON('{ hello: there }')
// -> { "hello": "there" }
```
Returns `undefined` if value passed in is not parsable
Throws if value passed in is not parsable.

@@ -38,2 +42,10 @@ ## Other options

This package was built for nicer arg parser for CLIs. e.g.
```
my-cli-command --data '{ foo: bar }'
```
If you need a JSON parser for your server, consider the `safeParse` export instead.
This package makes use of regular expressions when fixing malformed json. As a result, it may be vulnerable to a [REDOS attack](https://snyk.io/blog/redos-and-catastrophic-backtracking).

@@ -40,0 +52,0 @@

const { isBalanced, trimQuotes, isNull } = require('./utils')
module.exports.parseJSON = function parseJSON(x, defaultValue) {
const value = (typeof x === 'string') ? coerceStr(x, defaultValue) : coerceToString(x)
try {
if (isNull(value) && defaultValue) return defaultValue
return JSON.parse(value)
} catch (e) {
// console.log(`JSON.parse failed: ${e.message}`)
// console.log(value)
try {
const stringify = JSON.stringify(trimQuotes(value))
return JSON.parse(stringify)
} catch (e) {
return defaultValue
}
}
}
const DEBUG = false
const log = (DEBUG) ? log : () => {}

@@ -34,3 +20,148 @@ module.exports.safeParse = function simpleParse(data, defaultValue) {

function getType(value, defaultValue) {
module.exports.parseJSON = function parseJSON(input, defaultValue) {
if (isNull(input) || input === '' || input === undefined) {
return defaultValue || input
}
const value = (typeof input === 'string') ? coerceStr(input, defaultValue) : coerceToString(input)
if (value === 'true') {
return true
}
if (value === 'false') {
return false
}
const [err, first ] = parse(value)
// log('trimmed', trimmed)
if (first) {
log('first', first)
if (!needsMoreProcessing(first)) {
return first
}
}
const trimmed = trimQuotes(value)
// log('trimmed', trimmed)
const [errTwo, second ] = parse(trimmed)
if (second) {
log('second', second)
if (!needsMoreProcessing(second)) {
return second
}
}
const fixString = convertStringObjectToJsonString(trimmed)
// log('fixString', fixString)
const [errThree, third ] = parse(fixString)
if (third) {
log('third', third)
if (!needsMoreProcessing(third)) {
return third
}
}
const foo = coerceStr(fixString, defaultValue)
log('foo', foo)
const what = fixEscapedKeys(foo)
log('what', what)
const [errFour, four ] = parse(what)
if (four) {
log('four', four)
if (!needsMoreProcessing(four)) {
return four
}
}
const final = trimQuotes(what.replace(/'/g, '"'))
const [errFive, five ] = parse(final)
if (five) {
log('five', five)
if (!needsMoreProcessing(five)) {
return five
}
}
// Wrap values missing quotes { cool: nice }
const newer = final
// Temporarily stash boolean values
.replace(/:\s?(true+?)\s?/g, ': "TRUE_PLACEHOLDER"')
.replace(/:\s?(false+?)\s?/g, ': "FALSE_PLACEHOLDER"')
// .replace(/\[\s?(true+?)\s?\]/, 'TRUE_PLACEHOLDER')
// .replace(/\[\s?(false+?)\s?\]/, 'FALSE_PLACEHOLDER')
.replace(/\[\s?([A-Za-z]+?)\s?\]/, '[ "$1" ]')
.replace(/:\s?([A-Za-z]+?)\s}/g, ': "$1" }')
// log('newer', newer)
// Wrap values missing quotes
const newerStill = newer
.replace(/:\s?([A-Za-z]+?)\s?,/g, ': "$1",')
// Reset Temporarily stashed boolean values
.replace(/:\s?("TRUE_PLACEHOLDER"+?)\s?/g, ': true')
.replace(/:\s?("FALSE_PLACEHOLDER"+?)\s?/g, ': false')
// trailing booleans
// log('newerStill', newerStill)
const [errSeven, six ] = parse(newerStill)
if (six) {
log('six', six)
return six
}
// Attempt final rebalance
const balance = coerceStr(newerStill, defaultValue)
.replace(/\sfalse"}$/, ' false}')
.replace(/\strue"}$/, ' true}')
const [errSix, seven ] = parse(balance)
if (seven) {
log('seven', seven)
return seven
}
throw new Error('Unable to parse JSON')
}
function fixEscapedKeys(value) {
return value.replace(/\s\\"/g, '"').replace(/\\"\:/, '":')
}
function parse(value) {
let result, error
try {
result = JSON.parse(value)
} catch (err) {
// log('err', err)
error = err
}
return [ error, result ]
}
function convertStringObjectToJsonString(str) {
return str.replace(/(\w+:)|(\w+ :)/g, (matchedStr) => {
return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":'
})
}
/*
function convertValuesObjectToJsonString(str) {
return str.replace(/:(\w+')|(:\w+ ')/g, (matchedStr) => {
return ": '" + matchedStr.substring(0, matchedStr.length - 1) + "'"
})
}
*/
function needsMoreProcessing(value) {
if (typeof value !== 'string') {
return false
}
const stringType = getStringType(value)
return stringType === 'array' || stringType === 'object'
}
function getStringType(value, defaultValue) {
if (Array.isArray(value) || Array.isArray(defaultValue)) {

@@ -57,3 +188,3 @@ return 'array'

let rawVal = trimQuotes(str)
const type = getType(str, defaultReturn)
const type = getStringType(str, defaultReturn)
if ((type === 'array' || type === 'object') && !isBalanced(str)) {

@@ -95,5 +226,5 @@ // Find and try to fix mismatch brackets

function invertQuotes(str, quoteType, objectType) {
// console.log('Original', str)
// log('Original', str)
// const replaceOuterQuotes = new RegExp(`(${quoteType})(?=(?:[^${quoteType}]|${quoteType}[^]*${quoteType})*)`, 'g')
// console.log('replaceOuterQuotes', replaceOuterQuotes)
// log('replaceOuterQuotes', replaceOuterQuotes)
const quotePairsRegex = new RegExp(`${quoteType}[^\\\\${quoteType}]*(\\\\${quoteType}[^\\\\${quoteType}]*)*${quoteType}`, 'g')

@@ -109,3 +240,3 @@

.replace(/"/g, 'INNERDOUBLEQUOTE')
// console.log('redactedString', redactedString)
// log('redactedString', redactedString)
const repInner = (objectType === 'array') ? '"' : `\\"`

@@ -116,3 +247,3 @@ const fixed = redactedString

.replace(/INNERDOUBLEQUOTE/g, repInner)
// console.log('fixed', fixed)
// log('fixed', fixed)
return fixed

@@ -126,7 +257,7 @@ }

const replaceInnerConflict = new RegExp(`${quoteType}`, 'g')
// console.log('replaceInnerConflict', replaceInnerConflict)
// log('replaceInnerConflict', replaceInnerConflict)
const replaceInverseStart = new RegExp(`^${inverse}`)
// console.log('replaceInverseStart', replaceInverseStart)
// log('replaceInverseStart', replaceInverseStart)
const replaceInverseEnd = new RegExp(`${inverse}$`)
// console.log('replaceInverseEnd', replaceInverseEnd)
// log('replaceInverseEnd', replaceInverseEnd)
const fix = curr

@@ -151,11 +282,11 @@ // replace inner "

if (Array.isArray(val)) {
// console.log(`Converting Array into string`)
// log(`Converting Array into string`)
return JSON.stringify(val)
}
if (type === 'object') {
// console.log(`Converting Object into string`)
// log(`Converting Object into string`)
return JSON.stringify(val)
}
if (type === 'boolean') {
// console.log(`Converting Boolean into string`)
// log(`Converting Boolean into string`)
return JSON.stringify(val)

@@ -162,0 +293,0 @@ }

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