Socket
Socket
Sign inDemoInstall

js-schema

Package Overview
Dependencies
4
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.3.0 to 0.4.0

lib/patterns/class.js

9

index.js

@@ -5,14 +5,9 @@ module.exports = require('./lib/schema')

require('./lib/patterns/reference')
require('./lib/patterns/nothing')
require('./lib/patterns/anything')
require('./lib/patterns/object')
require('./lib/patterns/or')
require('./lib/patterns/instanceof')
require('./lib/patterns/schema')
require('./lib/patterns/equality')
require('./lib/patterns/regexp')
require('./lib/patterns/class')

@@ -19,0 +14,0 @@ // Extensions

var schema = require('../schema')
, EqualitySchema = require('../patterns/equality')
var ArraySchema = module.exports = function(itemSchema, max, min) {
if (itemSchema !== undefined) this.itemSchema = schema(itemSchema)
this.itemSchema = itemSchema
this.min = min

@@ -66,2 +67,11 @@ this.max = max

schema.fromJSON.def(function(sch) {
if (!sch || sch.type !== 'array') return
// Tuple typing is not yet supported
if (sch.items instanceof Array) return
return new ArraySchema(schema.fromJSON(sch.items), sch.maxItems, sch.minItems)
})
Array.of = function() {

@@ -73,3 +83,8 @@ // Possible signatures : (schema)

if (args.length === 2) args[2] = args[1]
return new ArraySchema(args[0], args[1], args[2])
return new ArraySchema(schema(args[0]), args[1], args[2])
}
Array.like = function(other) {
return new EqualitySchema(other)
}

@@ -21,3 +21,11 @@ var schema = require('../schema')

Boolean.schema = new BooleanSchema()
var booleanSchema = new BooleanSchema()
schema.fromJSON.def(function(sch) {
if (!sch || sch.type !== 'boolean') return
return booleanSchema
})
Boolean.schema = booleanSchema
Boolean.generate = Boolean.schema.generate
var ReferenceSchema = require('../patterns/reference')
, ClassSchema = require('../patterns/class')
Function.Reference = function(f) {
// Function creation is not possible the way patterns/class.js does,
// so overriding it here
Function.schema = new ClassSchema(Function)
Function.schema.generate = function() {
return function() {}
}
Function.reference = function(f) {
return new ReferenceSchema(f)
}

@@ -18,11 +18,11 @@ var schema = require('../schema')

if (this.minimum !== undefined) {
if (this.minimum !== undefined && this.minimum !== -Infinity) {
checks.push('instance ' + (this.exclusiveMinimum ? '>' : '>=') + ' {0}')
}
if (this.maximum !== undefined) {
if (this.maximum !== undefined && this.maximum !== Infinity) {
checks.push('instance ' + (this.exclusiveMaximum ? '<' : '<=') + ' {1}')
}
if (this.divisibleBy !== undefined) {
if (this.divisibleBy !== undefined && this.divisibleBy !== 0) {
checks.push('instance % {2} === 0')

@@ -97,7 +97,8 @@ }

toJSON : function() {
var schema = { type : 'number' }
var integer = this.divisibleBy !== 0 && this.divisibleBy === Math.floor(this.divisibleBy)
var schema = { type : integer ? 'integer' : 'number' }
if (this.minimum !== undefined) {
schema.minimum = this.minimum
schema.exclusiveMinimum = (this.exclusiveMinimum === true)
if (this.exclusiveMinimum === true) schema.exclusiveMinimum = true
}

@@ -107,6 +108,7 @@

schema.maximum = this.maximum
schema.exclusiveMaximum = (this.exclusiveMaximum === true)
if (this.exclusiveMaximum === true) schema.exclusiveMaximum = true
}
if (this.divisibleBy !== undefined) schema.divisibleBy = this.divisibleBy
var step = this.divisibleBy
if (step !== undefined && step !== 0 && step !== 1) schema.divisibleBy = step

@@ -117,2 +119,11 @@ return schema

schema.fromJSON.def(function(sch) {
if (!sch || (sch.type !== 'number' && sch.type !== 'integer')) return
return new NumberSchema( sch.minimum, sch.exclusiveMinimum
, sch.maximum, sch.exclusiveMaximum
, sch.divisibleBy || (sch.type === 'integer' ? 1 : 0)
)
})
Number.schema = new NumberSchema()

@@ -119,0 +130,0 @@ Number.min = Number.schema.min

var ReferenceSchema = require('../patterns/reference')
, EqualitySchema = require('../patterns/equality')
Object.Reference = function(o) {
Object.like = function(other) {
return new EqualitySchema(other)
}
Object.reference = function(o) {
return new ReferenceSchema(o)
}

@@ -1,22 +0,17 @@

var schema = require('../schema')
var RegexpSchema = require('../patterns/regexp')
var StringSchema = function() {
return new schema(this)
}
StringSchema.prototype = {
compile : function() {
return { expression : 'Object(instance) instanceof String' }
},
String.of = function() {
// Possible signatures : (charset)
// (length, charset)
// (minLength, maxLength, charset)
var args = Array.prototype.reverse.call(arguments)
, charset = args[0] ? ('[' + args[0] + ']') : '[a-zA-Z0-9]'
, max = args[1]
, min = (args.length > 2) ? args[2] : args[1]
, regexp = '^' + charset + '{' + (min || 0) + ',' + (max || '') + '}$'
generate : function() {
return schema(/[a-zA-Z0-9]+/).generate()
},
toJSON : function() {
return { type : 'string' }
}
return new RegexpSchema(RegExp(regexp))
}
String.schema = new StringSchema()
String.schema = new RegexpSchema()
String.generate = String.schema.generate

@@ -34,2 +34,6 @@ var native_functions = [Boolean, Number, String, Object, Array, Function, Date]

resolved = reference.name + '$1'
} else if (reference instanceof RegExp) {
// regexps can be converted to strings easily
resolved = reference.toString() + '$1'

@@ -36,0 +40,0 @@ } else {

var schema = require('../schema')
, RandExp = require('randexp')
, utils = require('../utils')
var ObjectSchema = module.exports = function(regexp, other) {
var ObjectSchema = module.exports = function(properties, other) {
var self = this
this.other = other
this.regexp = regexp || []
for (var i in this.regexp) this.regexp[i].key = RegExp('^' + this.regexp[i].key + '$')
this.properties = properties || []
// Sorting properties into two groups
this.stringProps = {}, this.regexpProps = []
this.properties.forEach(function(property) {
if (typeof property.key === 'string') {
self.stringProps[property.key] = property
} else {
self.regexpProps.push(property)
}
})
return new schema(this)

@@ -13,53 +26,83 @@ }

ObjectSchema.prototype = {
validate : function(instance) {
// TODO: treat non-regexps differently
// regexpElementTests = '[](){}^$?*+'.split('').map(function(element) {
// return RegExp('(^|[^\\\\])\\' + element)
// })
var i, matches = this.regexp.map(function(){ return 0 }), match = false
for (var property in instance) {
match = false
for (i in this.regexp) {
if (this.regexp[i].key.test(property)) {
if (!this.regexp[i].value(instance[property])) return false
matches[i] += 1
match = true
}
compile : function() {
var checks = ['instance != null']
, references = []
, ref = utils.referenceHandler(references)
// Simple string properties
var check
for (var key in this.stringProps) {
check = '{schema}(instance[{key}])'.replace('{schema}', ref(this.stringProps[key].value))
.replace('{key}', ref(key))
if (this.stringProps[key].min === 0) {
check = '(instance[{key}] === undefined || {check})'.replace('{key}', ref(key))
.replace('{check}', check)
}
if (!match && this.other && !this.other(instance[property])) return false
checks.push(check)
}
for (i in this.regexp) {
if (!(this.regexp[i].min <= matches[i] && matches[i] <= this.regexp[i].max)) return false
if (!this.regexpProps.length && !this.other) {
return { references : references, expression : checks.join(' && ') }
}
// Regexp and other properties
var stringPropNames = Object.keys(this.stringProps)
var fn = 'if (!( {checks} )) return false;'.replace('{checks}', checks.join(' && '))
if (this.other) fn += 'var checked;'
// Iterating over the keys in the instance
fn += 'for (var key in instance) {'
return true
// Checking the key against every key regexps
if (this.other) fn += 'checked = false;'
for (var i = 0; i < this.regexpProps.length; i++) {
if (this.other) {
check = 'if (({regexp}.test(key) && (checked = true)) && !{schema}(instance[key])) return false;'
} else {
check = 'if ({regexp}.test(key) && !{schema}(instance[key])) return false;'
}
fn += check.replace('{regexp}', ref(this.regexpProps[i].key))
.replace('{schema}', ref(this.regexpProps[i].value))
}
// If the key is not matched by regexps and by simple string checks, check it against this.other
if (this.other) {
check = 'if (!checked && {stringProps}.indexOf(key) === -1 && !{other}(instance[key])) return false;'
fn += check.replace('{stringProps}', ref(stringPropNames))
.replace('{other}', ref(this.other))
}
fn += '}'
// Iteration ends
// If all checks passed, the instance conforms to the schema
fn += 'return true;'
return { references : references, fn : fn }
},
generate : function() {
var i, key, object = {}
var object = {}
for (i in this.regexp) {
var n = 0
for (key in object) {
if (this.regexp[i].key.test(key)) n += 1
for (var key in this.stringProps) {
if (this.stringProps[key].min === 1 || Math.random() < 0.5) {
object[key] = this.stringProps[key].value.generate()
}
}
var n, property, randexp
for (var i = 0; i < this.regexpProps.length; i++) {
property = this.regexpProps[i]
n = Object.keys(object).filter(function(ikey){ return property.key.test(ikey) }).length
while (n < this.regexp[i].min) {
key = new RandExp(this.regexp[i].key).gen()
if (!(key in object)) {
object[key] = this.regexp[i].value.generate()
n += 1
}
randexp = new RandExp(property.key)
while (n < property.min || (n < property.max && Math.random() < 0.5)) {
key = randexp.gen()
if (key in object) continue
object[key] = property.value.generate()
n += 1
}
while (n < this.regexp[i].max && Math.random() < 0.5) {
key = new RandExp(this.regexp[i].key).gen()
if (!(key in object)) {
object[key] = this.regexp[i].value.generate()
n += 1
}
}
}

@@ -75,9 +118,76 @@

return object
},
toJSON : function() {
var i, property, regexp, json = { type : 'object' }
for (i in this.stringProps) {
property = this.stringProps[i]
json.properties = json.properties || {}
json.properties[property.key] = property.value.toJSON()
if (property.min === 1) json.properties[property.key].required = true
}
for (i = 0; i < this.regexpProps.length; i++) {
property = this.regexpProps[i]
json.patternProperties = json.patternProperties || {}
regexp = property.key.toString()
regexp = regexp.substr(2, regexp.length - 4)
json.patternProperties[regexp] = property.value.toJSON()
}
if (this.other && this.other !== schema(undefined)) {
json.additionalProperties = (this.other === schema(null)) ? false : this.other.toJSON()
}
return json
}
}
// Testing if a given string is a real regexp or just a single string escaped
// If it is just a string escaped, return the string. Otherwise return the regexp
var regexpString = global.regexpString = (function() {
// Special characters that should be escaped when describing a regular string in regexp
var shouldBeEscaped = '[](){}^$?*+.'.split('').map(function(element) {
return RegExp('(\\\\)*\\' + element, 'g')
})
// Special characters that shouldn't be escaped when describing a regular string in regexp
var shouldntBeEscaped = 'bBwWdDsS'.split('').map(function(element) {
return RegExp('(\\\\)*' + element, 'g')
})
return function(string) {
var i, j, match
for (i = 0; i < shouldBeEscaped.length; i++) {
match = string.match(shouldBeEscaped[i])
if (!match) continue
for (j = 0; j < match.length; j++) {
// If it is not escaped, it must be a regexp (e.g. [, \\[, \\\\[, etc.)
if (match[j].length % 2 === 1) return RegExp('^' + string + '$')
}
}
for (i = 0; i < shouldntBeEscaped.length; i++) {
match = string.match(shouldntBeEscaped[i])
if (!match) continue
for (j = 0; j < match.length; j++) {
// If it is escaped, it must be a regexp (e.g. \b, \\\b, \\\\\b, etc.)
if (match[j].length % 2 === 0) return RegExp('^' + string + '$')
}
}
// It is not a real regexp. Removing the escaping.
for (i = 0; i < shouldBeEscaped.length; i++) {
string = string.replace(shouldBeEscaped[i], function(match) {
return match.substr(1)
})
}
return string
}
})()
schema.fromJS.def(function(object) {
if (!(object instanceof Object)) return
var first, min, max, value, properties = [], other

@@ -87,2 +197,7 @@ for (var key in object) {

if (key === '*') {
other = value
continue
}
first = key[0]

@@ -93,7 +208,5 @@ min = (first === '*' || first === '?') ? 0 : 1

if (key === '') {
other = value
} else {
properties.push({ min : min, max : max, key : key, value : value })
}
key = regexpString(key)
properties.push({ min : min, max : max, key : key, value : value })
}

@@ -103,1 +216,28 @@

})
schema.fromJSON.def(function(sch) {
if (!sch || sch.type !== 'object') return
var key, properties = []
for (key in sch.properties) {
properties.push({ min : sch.properties[key].required ? 1 : 0
, max : 1
, key : key
, value : schema.fromJSON(sch.properties[key])
})
}
for (key in sch.patternProperties) {
properties.push({ min : 0
, max : Infinity
, key : RegExp('^' + key + '$')
, value : schema.fromJSON(sch.properties[key])
})
}
var other
if (sch.additionalProperties !== undefined) {
other = sch.additionalProperties === false ? schema(null) : schema.fromJSON(sch.additionalProperties)
}
return new ObjectSchema(properties, other)
})
var schema = require('../schema')
, EqualitySchema = require('../patterns/equality')
var OrSchema = module.exports = function(schemas) {
this.schemas = schemas.map(schema.fromJS)
this.schemas = schemas

@@ -21,2 +22,20 @@ return new schema(this)

return this.schemas[Math.floor(Math.random()*this.schemas.length)].generate()
},
toJSON : function() {
var jsons = this.schemas.map(schema.toJSON)
var onlyEquality = true
for (var i = 0; i < jsons.length; i++) {
if (!(jsons[i]['enum'] instanceof Array && jsons[i]['enum'].length === 1)) {
onlyEquality = false
break
}
}
if (onlyEquality) return { 'enum' : jsons.map(function(json) { return json['enum'][0] }) }
return { 'type' : jsons.map(function(json) {
var simpleType = typeof json.type === 'string' && Object.keys(json).length === 1
return simpleType ? json.type : json
})}
}

@@ -27,3 +46,19 @@ }

schema.fromJS.def(function(schemas) {
if (schemas instanceof Array) return new OrSchema(schemas)
if (schemas instanceof Array) return new OrSchema(schemas.map(schema.fromJS))
})
schema.fromJSON.def(function(sch) {
if (!sch) return
if (sch['enum'] instanceof Array) {
return new OrSchema(sch['enum'].map(function(object) {
return new EqualitySchema(object)
}))
}
if (sch['type'] instanceof Array) {
return new OrSchema(sch['type'].map(function(type) {
return schema.fromJSON(typeof type === 'string' ? { type : type } : type)
}))
}
})
var schema = require('../schema')
, RandExp = require('randexp')
var defaultRandExp = new RandExp(/[a-zA-Z0-9]*/)
var RegexpSchema = module.exports = function(regexp) {
this.regexp = regexp
this.randexp = this.regexp ? new RandExp(this.regexp) : defaultRandExp

@@ -12,10 +15,36 @@ return new schema(this)

compile : function() {
return { references : [this.regexp], expression : '{0}.test(instance)' }
return { references : [this.regexp]
, expression : 'Object(instance) instanceof String'
+ (this.regexp ? ' && {0}.test(instance)' : '')
}
},
generate : function() {
return (new RandExp(this.regexp)).gen()
return this.randexp.gen()
},
toJSON : function() {
var sch = { type : 'string' }
if (this.regexp) {
console.log(this.regexp.toString())
sch.pattern = this.regexp.toString()
sch.pattern = sch.pattern.substr(1, sch.pattern.length - 2)
}
return sch
}
}
schema.fromJSON.def(function(sch) {
if (!sch || sch.type !== 'string') return
if ('pattern' in sch) {
return new RegexpSchema(RegExp('^' + sch.pattern + '$'))
} else if ('minLength' in sch || 'maxLength' in sch) {
return new RegexpSchema(RegExp('^.{' + (sch.minLength || 0) + ',' + (sch.maxLength || '') + '}$'))
} else {
return new RegexpSchema()
}
})

@@ -22,0 +51,0 @@ schema.fromJS.def(function(regexp) {

{
"author": "Gábor Molnár <gabor.molnar@sch.bme.hu>",
"name": "js-schema",
"description": "A simple and easy to use JSON Schema library.",
"description": "A simple and easy to use schema library.",
"keywords": [
"schema",
"json",
"random generator"
"JSON Schema",
"random generator",
"object generation",
"testing"
],
"version": "0.3.0",
"version": "0.4.0",
"homepage": "https://github.com/molnarg/js-schema",

@@ -17,3 +19,3 @@ "repository": {

"dependencies": {
"def.js" : "0.1.4",
"def.js" : "0.1.5",
"randexp" : "*"

@@ -20,0 +22,0 @@ },

js-schema
=========
js-schema is a new way of describing object schemas in JavaScript. It has a clean and simple syntax.
Usecases include object validation and random object generation.
js-schema is a new way of describing object schemas in JavaScript. It has a clean and simple syntax,
and it is capable of serializing to/from the popular JSON Schema format. Typical usecases include
object validation and random object generation.

@@ -42,4 +43,4 @@ A simple example

var testcases = Array.of(10, Duck).generate();
test(testcases);
var testcases = Array.of(5, Duck).generate();
console.log(testcases);
```

@@ -67,3 +68,4 @@

For serialization to JSON Schema use the `toJSON()` method of any schema. For deserialization
use `schema.fromJSON()`. _Warning_: JSON support is still incomplete.
use `schema.fromJSON(json)`. JSON Schema support is still incomplete, but it can reliably
deserialize JSON Schemas generated by js-schema itself.

@@ -75,14 +77,15 @@ Patterns

There are 8 basic rules used by js-schema:
There are 9 basic rules used for describing schemas:
1. `Class` (where `Class` is a function, and has a function type property called
`schema`) matches `x` if `Class.schema(x)` is true
2. `Class` (where `Class` is a function) matches `x` if `x instanceof Class`
3. `/regexp/` matches `x` if `/regexp/.test(x) === true`
4. `[pattern1, pattern2, ...]` matches `x` if _any_ of the given patterns match `x`
5. `{ 'a' : pattern1, 'b' : pattern2, ... }` matches `x` if `pattern1` matches `x.a`,
1. `Class` (where `Class` is a function, and has a function type property called `schema`)
matches `x` if `Class.schema(x) === true`.
2. `Class` (where `Class` is a function) matches `x` if `x instanceof Class`.
3. `/regexp/` matches `x` if `/regexp/.test(x) === true`.
4. `[object]` matches `x` if `x` is deep equal to `object`
5. `[pattern1, pattern2, ...]` matches `x` if _any_ of the given patterns match `x`.
6. `{ 'a' : pattern1, 'b' : pattern2, ... }` matches `x` if `pattern1` matches `x.a`,
`pattern2` matches `x.b`, etc. For details see the object pattern subsection.
6. `undefined` matches `x` if `x` _is not_ `null` or `undefined`
7. `null` matches `x` if `x` _is_ `null` or `undefined`
8. `primitive` (where `primitive` is boolean, number, or string) matches `x` if `primitive === x`
7. `undefined` matches `x` if `x` _is not_ `null` or `undefined`.
8. `null` matches `x` if `x` _is_ `null` or `undefined`.
9. `primitive` (where `primitive` is boolean, number, or string) matches `x` if `primitive === x`.

@@ -95,13 +98,17 @@ The order is important. When calling `schema(pattern)`, the rules are examined one by one,

The following example contains patterns for all of the rules, except the first. The comments
The following example contains patterns for all of the rules. The comments
denote the number of the rules used and the nesting level of the subpatterns (indentation).
```javascript
validate = schema({ // (5) 'object' pattern
a : [Color, 'red', 'blue'], // (4) 'or' pattern
// (2) 'instanceof' pattern
// (8) 'primitive' pattern
b : /The meaning of life is \d+/, // (3) regexp pattern
c : undefined, // (6) 'anything' pattern
d : null // (7) 'nothing' pattern
var Color = function() {}, x = { /* ... */ };
var validate = schema({ // (6) 'object' pattern
a : [ Color, 'red', 'blue', [[0,0,0]] ], // (5) 'or' pattern
// (2) 'instanceof' pattern
// (9) 'primitive' pattern
// (4) 'deep equality' pattern
b : Number, // (1) 'class schema' pattern
c : /The meaning of life is \d+/, // (3) regexp pattern
d : undefined, // (7) 'anything' pattern
e : null // (8) 'nothing' pattern
});

@@ -113,6 +120,8 @@

`validate(x)` returns true if all of these are true:
* `x.a` is either 'red' or 'blue' or an instance of the Color class
* `x.b` is a string that matches the /The meaning of life is \d+/ regexp
* `x` does have a property called `c`
* `x` doesn't have a property called `d`, or it does but it is null or undefined
* `x.a` is either 'red', 'blue', an instance of the Color class,
or an array that is exactly like `[0,0,0]`
* `x.b` conforms to Number.schema (it return true if `x.b instanceof Number`)
* `x.c` is a string that matches the /The meaning of life is \d+/ regexp
* `x` does have a property called `d`
* `x` doesn't have a property called `e`, or it does but it is `null` or `undefined`

@@ -135,3 +144,3 @@ ### The object pattern ###

validate = schema({
var validate = schema({
'name' : String, // x.name must be string

@@ -157,10 +166,14 @@ 'colou?r' : String // x must have a string type property called either

`Object.Reference(object)` matches `x` if `x === object`.
`Object.reference(object)` matches `x` if `x === object`.
`Object.like(object)` matches `x` if `x` deep equals `object`.
### Functions ###
`Function.Reference(func)` matches `x` if `x === func`.
`Function.reference(func)` matches `x` if `x === func`.
### Arrays ###
The `Array.like(array)` matches `x` if `x instanceof Array` and it deep equals `array`.
The `Array.of` method has three signatures:

@@ -177,2 +190,15 @@ - `Array.of(pattern)` matches `x` if `x instanceof Array` and `pattern` matches every element of `x`.

### Strings ###
The `String.of` method has three signatures:
- `String.of(charset)` matches `x` if it is a string and contains characters that are included in `charset`
- `String.of(length, charset)` additionally checks the length of the instance and returns true only if it equals to `length`.
- `String.of(minLength, maxLength, charset)` is similar, but checks if the length is in the given interval.
`charset` must be given in a format that can be directly inserted in a regular expression when
wrapped by `[]`. For example, `'abc'` means a character set containing the first 3 lowercase letters
of the english alphabet, while `'a-zA-C'` means a character set of all english lowercase letters,
and the first 3 uppercase letters. If `charset` is `undefined` then the `a-zA-Z0-9` character set
is used.
Future plans

@@ -182,10 +208,17 @@ ============

Better JSON Schema support. js-schema should be able to parse any valid JSON schema and generate
JSON Schema for most of the patterns (this is not possible in general, e.g. patterns that have
external references like the instanceof pattern).
JSON Schema for most of the patterns (this is not possible in general, because of patterns that hold
external references like the 'instanceof' pattern).
Using the random object generation, it should be possible to build a QucikCheck-like testing
framework, which could be used to generate testcases for js-schema (yes, I like resursion).
Defining and validating resursive data structures:
```javascript
// defining the data structure
// Defining the data structure:
var Tree = schema({ left : [Tree, Number], right : [Tree, Number] });
// The schema function gets this as argument:
// { left : [undefined, Number], right : [undefined, Number] }
// Since providing 'undefined' as part of an 'or' patterns doesn't make sense,
// it must be a self-reference. Self-reference usually occur as part of 'or' patterns.

@@ -197,10 +230,6 @@ // validation

Using the random object generation, it should be possible to build a QucikCheck-like testing
framework, which could be used to generate testcases for js-schema (yes, I like resursion).
Contributing
============
Feel free to open an issue if you would like to help imporving js-schema, find a bug, or even
if there's a feature that would be useful for you but don't have the time to implement it yourself.
Feel free to open an issue if you would like to help imporving js-schema or find a bug.

@@ -220,2 +249,1 @@ Installation

Copyright (C) 2012 Gábor Molnár <gabor.molnar@sch.bme.hu>
SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc