Gathering detailed insights and metrics for fakerest
Gathering detailed insights and metrics for fakerest
Gathering detailed insights and metrics for fakerest
Gathering detailed insights and metrics for fakerest
npm install fakerest
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
439 Stars
320 Commits
48 Forks
19 Watching
3 Branches
21 Contributors
Updated on 07 Nov 2024
Minified
Minified + Gzipped
TypeScript (92.49%)
JavaScript (4.53%)
HTML (2.66%)
Makefile (0.28%)
Shell (0.04%)
Cumulative downloads
Total Downloads
Last day
12.1%
1,222
Compared to previous day
Last week
3.1%
6,631
Compared to previous week
Last month
44.7%
24,481
Compared to previous month
Last year
-16%
213,390
Compared to previous year
A browser library that intercepts AJAX calls to mock a REST server based on JSON data.
Use it in conjunction with MSW, fetch-mock, or Sinon.js to test JavaScript REST clients on the client side (e.g. single page apps) without a server.
See it in action in the react-admin demo (source code).
1npm install fakerest --save-dev
FakeRest lets you create a handler function that you can pass to an API mocking library. FakeRest supports MSW, fetch-mock, and Sinon. If you have the choice, we recommend using MSW, as it will allow you to inspect requests as you usually do in the dev tools network tab.
Install MSW and initialize it:
1npm install msw@latest --save-dev
2npx msw init <PUBLIC_DIR> # eg: public
Then configure an MSW worker:
1// in ./src/fakeServer.js 2import { http } from 'msw'; 3import { setupWorker } from "msw/browser"; 4import { getMswHandler } from "fakerest"; 5 6const handler = getMswHandler({ 7 baseUrl: 'http://localhost:3000', 8 data: { 9 'authors': [ 10 { id: 0, first_name: 'Leo', last_name: 'Tolstoi' }, 11 { id: 1, first_name: 'Jane', last_name: 'Austen' } 12 ], 13 'books': [ 14 { id: 0, author_id: 0, title: 'Anna Karenina' }, 15 { id: 1, author_id: 0, title: 'War and Peace' }, 16 { id: 2, author_id: 1, title: 'Pride and Prejudice' }, 17 { id: 3, author_id: 1, title: 'Sense and Sensibility' } 18 ], 19 'settings': { 20 language: 'english', 21 preferred_format: 'hardback', 22 } 23 } 24}); 25export const worker = setupWorker( 26 // Make sure you use a RegExp to target all calls to the API 27 http.all(/http:\/\/localhost:3000/, handler) 28);
Finally, call the worker.start()
method before rendering your application. For instance, in a Vite React application:
1import React from "react"; 2import ReactDom from "react-dom"; 3import { App } from "./App"; 4import { worker } from "./fakeServer"; 5 6worker.start({ 7 quiet: true, // Instruct MSW to not log requests in the console 8 onUnhandledRequest: 'bypass', // Instruct MSW to ignore requests we don't handle 9}).then(() => { 10 ReactDom.render(<App />, document.getElementById("root")); 11});
FakeRest will now intercept every fetch
request to the REST server.
Install fetch-mock:
1npm install fetch-mock --save-dev
You can then create a handler and pass it to fetch-mock:
1import fetchMock from 'fetch-mock'; 2import { getFetchMockHandler } from "fakerest"; 3 4const handler = getFetchMockHandler({ 5 baseUrl: 'http://localhost:3000', 6 data: { 7 'authors': [ 8 { id: 0, first_name: 'Leo', last_name: 'Tolstoi' }, 9 { id: 1, first_name: 'Jane', last_name: 'Austen' } 10 ], 11 'books': [ 12 { id: 0, author_id: 0, title: 'Anna Karenina' }, 13 { id: 1, author_id: 0, title: 'War and Peace' }, 14 { id: 2, author_id: 1, title: 'Pride and Prejudice' }, 15 { id: 3, author_id: 1, title: 'Sense and Sensibility' } 16 ], 17 'settings': { 18 language: 'english', 19 preferred_format: 'hardback', 20 } 21 } 22}); 23 24fetchMock.mock('begin:http://localhost:3000', handler);
FakeRest will now intercept every fetch
request to the REST server.
Install Sinon:
1npm install sinon --save-dev
Then, configure a Sinon server:
1import sinon from 'sinon'; 2import { getSinonHandler } from "fakerest"; 3 4const handler = getSinonHandler({ 5 baseUrl: 'http://localhost:3000', 6 data: { 7 'authors': [ 8 { id: 0, first_name: 'Leo', last_name: 'Tolstoi' }, 9 { id: 1, first_name: 'Jane', last_name: 'Austen' } 10 ], 11 'books': [ 12 { id: 0, author_id: 0, title: 'Anna Karenina' }, 13 { id: 1, author_id: 0, title: 'War and Peace' }, 14 { id: 2, author_id: 1, title: 'Pride and Prejudice' }, 15 { id: 3, author_id: 1, title: 'Sense and Sensibility' } 16 ], 17 'settings': { 18 language: 'english', 19 preferred_format: 'hardback', 20 } 21 }, 22}); 23 24// use sinon.js to monkey-patch XmlHttpRequest 25const sinonServer = sinon.fakeServer.create(); 26// this is required when doing asynchronous XmlHttpRequest 27sinonServer.autoRespond = true; 28sinonServer.respondWith(handler);
FakeRest will now intercept every XMLHttpRequest
request to the REST server.
FakeRest uses a simple REST syntax described below.
GET /[name]
returns an array of records in the name
collection. It accepts 4 query parameters: filter
, sort
, range
, and embed
. It responds with a status 200 if there is no pagination, or 206 if the list of items is paginated. The response mentions the total count in the Content-Range
header.
GET /books?filter={"author_id":1}&embed=["author"]&sort=["title","desc"]&range=[0-9]
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } },
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } }
]
The filter
param must be a serialized object literal describing the criteria to apply to the search query. See the supported filters for more details.
GET /books?filter={"author_id":1} // return books where author_id is equal to 1
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility" }
]
// array values are possible
GET /books?filter={"id":[2,3]} // return books where id is in [2,3]
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility" }
]
// use the special "q" filter to make a full-text search on all text fields
GET /books?filter={"q":"and"} // return books where any of the book properties contains the string 'and'
HTTP 1.1 200 OK
Content-Range: items 0-2/3
Content-Type: application/json
[
{ "id": 1, "author_id": 0, "title": "War and Peace" },
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility" }
]
// use _gt, _gte, _lte, _lt, or _neq suffix on filter names to make range queries
GET /books?filter={"price_lte":20} // return books where the price is less than or equal to 20
GET /books?filter={"price_gt":20} // return books where the price is greater than 20
// when the filter object contains more than one property, the criteria combine with an AND logic
GET /books?filter={"published_at_gte":"2015-06-12","published_at_lte":"2015-06-15"} // return books published between two dates
The sort
param must be a serialized array literal defining first the property used for sorting, then the sorting direction.
GET /author?sort=["date_of_birth","asc"] // return authors, the oldest first
GET /author?sort=["date_of_birth","desc"] // return authors, the youngest first
The range
param defines the number of results by specifying the rank of the first and last results. The first result is #0.
GET /books?range=[0-9] // return the first 10 books
GET /books?range=[10-19] // return the 10 next books
The embed
param sets the related objects or collections to be embedded in the response.
// embed author in books
GET /books?embed=["author"]
HTTP 1.1 200 OK
Content-Range: items 0-3/4
Content-Type: application/json
[
{ "id": 0, "author_id": 0, "title": "Anna Karenina", "author": { "id": 0, "first_name": "Leo", "last_name": "Tolstoi" } },
{ "id": 1, "author_id": 0, "title": "War and Peace", "author": { "id": 0, "first_name": "Leo", "last_name": "Tolstoi" } },
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } }
]
// embed books in author
GET /authors?embed=["books"]
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ id: 0, first_name: 'Leo', last_name: 'Tolstoi', books: [{ id: 0, author_id: 0, title: 'Anna Karenina' }, { id: 1, author_id: 0, title: 'War and Peace' }] },
{ id: 1, first_name: 'Jane', last_name: 'Austen', books: [{ id: 2, author_id: 1, title: 'Pride and Prejudice' }, { id: 3, author_id: 1, title: 'Sense and Sensibility' }] }
]
// you can embed several objects
GET /authors?embed=["books","country"]
GET /[name]/:id
returns a JSON object, and a status 200, unless the resource doesn't exist.
GET /books/2
HTTP 1.1 200 OK
Content-Type: application/json
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" }
The embed
param sets the related objects or collections to be embedded in the response.
GET /books/2?embed=['author']
HTTP 1.1 200 OK
Content-Type: application/json
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } }
POST /[name]
returns a status 201 with a Location
header for the newly created resource, and the new resource in the body.
POST /books
{ "author_id": 1, "title": "Emma" }
HTTP 1.1 201 Created
Location: /books/4
Content-Type: application/json
{ "author_id": 1, "title": "Emma", "id": 4 }
PUT /[name]/:id
returns the modified JSON object, and a status 200, unless the resource doesn't exist.
PUT /books/2
{ "author_id": 1, "title": "Pride and Prejudice" }
HTTP 1.1 200 OK
Content-Type: application/json
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" }
DELETE /[name]/:id
returns the deleted JSON object, and a status 200, unless the resource doesn't exist.
DELETE /books/2
HTTP 1.1 200 OK
Content-Type: application/json
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" }
Operators are specified as suffixes on each filtered field. For instance, applying the _lte
operator on the price
field for the books
resource is done like this:
GET /books?filter={"price_lte":20} // return books where the price is less than or equal to 20
_eq
: check for equality on simple values:
GET /books?filter={"price_eq":20} // return books where the price is equal to 20
_neq
: check for inequality on simple values
GET /books?filter={"price_neq":20} // return books where the price is not equal to 20
_eq_any
: check for equality on any passed values
GET /books?filter={"price_eq_any":[20, 30]} // return books where the price is equal to 20 or 30
_neq_any
: check for inequality on any passed values
GET /books?filter={"price_neq_any":[20, 30]} // return books where the price is not equal to 20 nor 30
_inc_any
: check for items that include any of the passed values
GET /books?filter={"authors_inc_any":['William Gibson', 'Pat Cadigan']} // return books where authors include either 'William Gibson' or 'Pat Cadigan' or both
_q
: check for items that contain the provided text
GET /books?filter={"author_q":['Gibson']} // return books where the author includes 'Gibson' not considering the other fields
_lt
: check for items that have a value lower than the provided value
GET /books?filter={"price_lte":100} // return books that have a price lower that 100
_lte
: check for items that have a value lower than or equal to the provided value
GET /books?filter={"price_lte":100} // return books that have a price lower or equal to 100
_gt
: check for items that have a value greater than the provided value
GET /books?filter={"price_gte":100} // return books that have a price greater that 100
_gte
: check for items that have a value greater than or equal to the provided value
GET /books?filter={"price_gte":100} // return books that have a price greater or equal to 100
FakeRest allows you to define a single element, such as a user profile or global settings, that can be fetched, updated, or deleted.
GET /settings
HTTP 1.1 200 OK
Content-Type: application/json
{ "language": "english", "preferred_format": "hardback" }
PUT /settings
{ "language": "french", "preferred_format": "paperback" }
HTTP 1.1 200 OK
Content-Type: application/json
{ "language": "french", "preferred_format": "paperback" }
DELETE /settings
HTTP 1.1 200 OK
Content-Type: application/json
{ "language": "french", "preferred_format": "paperback" }
Middlewares let you intercept requests and simulate server features such as:
You can define middlewares on all handlers, by passing a middlewares
option:
1import { getMswHandler } from 'fakerest'; 2import { data } from './data'; 3 4const handler = getMswHandler({ 5 baseUrl: 'http://my.custom.domain', 6 data, 7 middlewares: [ 8 async (context, next) => { 9 if (context.headers.Authorization === undefined) { 10 return { 11 status: 401, 12 headers: {}, 13 }; 14 } 15 16 return next(context); 17 }, 18 withDelay(300), 19 ], 20});
A middleware is a function that receives 2 parameters:
context
, an object containing the data extracted from the request that FakeRest uses to build the response. It has the following properties:method
: The request method as a string (GET
, POST
, PATCH
or PUT
)url
: The request URL as a stringheaders
: The request headers as an object where keys are header namesrequestBody
: The parsed request data if anyparams
: The request parameters from the URL search (e.g. the identifier of the requested record)collection
: The name of the targeted collection (e.g. posts
)single
: The name of the targeted single (e.g. settings
)next
function to call the next middleware in the chain, to which you must pass the context
A middleware must return a FakeRest response either by returning the result of the next
function or by returning its own response. A FakeRest response is an object with the following properties:
status
: The response status as a number (e.g. 200
)headers
: The response HTTP headers as an object where keys are header namesbody
: The response body which will be stringifiedHere's how to implement an authentication check:
1const handler = getMswHandler({ 2 baseUrl: 'http://my.custom.domain', 3 data, 4 middlewares: [ 5 async (context, next) => { 6 if (context.headers.Authorization === undefined) { 7 return { status: 401, headers: {} }; 8 } 9 return next(context); 10 } 11 ] 12});
Here's how to implement server-side validation:
1const handler = getMswHandler({ 2 baseUrl: 'http://my.custom.domain', 3 data, 4 middlewares: [ 5 async (context, next) => { 6 if ( 7 context.collection === "books" && 8 request.method === "POST" && 9 !context.requestBody?.title 10 ) { 11 return { 12 status: 400, 13 headers: {}, 14 body: { 15 errors: { 16 title: 'An article with this title already exists. The title must be unique.', 17 }, 18 }, 19 }; 20 } 21 22 return next(context); 23 } 24 ] 25});
Here's how to implement dynamically generated values on creation:
1const handler = getMswHandler({
2 baseUrl: 'http://my.custom.domain',
3 data,
4 middlewares: [
5 async (context, next) => {
6 if (
7 context.collection === 'books' &&
8 context.method === 'POST'
9 ) {
10 const response = await next(context);
11 response.body.updatedAt = new Date().toISOString();
12 return response;
13 }
14
15 return next(context);
16 }
17 ]
18});
Here's how to simulate response delays:
1const handler = getMswHandler({ 2 baseUrl: 'http://my.custom.domain', 3 data, 4 middlewares: [ 5 async (context, next) => { 6 return new Promise((resolve) => { 7 setTimeout(() => { 8 resolve(next(context)); 9 }, 500); 10 }); 11 } 12 ] 13});
This is so common FakeRest provides the withDelay
function for that:
1import { getMswHandler, withDelay } from 'fakerest';
2
3const handler = getMswHandler({
4 baseUrl: 'http://my.custom.domain',
5 data,
6 middlewares: [
7 withDelay(500), // delay in ms
8 ]
9});
All handlers can be customized to accommodate your API structure.
By default, FakeRest assumes all records have a unique id
field.
Some databases such as MongoDB use _id
instead of id
for collection identifiers.
You can customize FakeRest to do the same by setting the identifierName
option:
1const handler = getMswHandler({
2 baseUrl: 'http://my.custom.domain',
3 data,
4 identifierName: '_id'
5});
You can also specify that on a per-collection basis:
1import { MswAdapter, Collection } from 'fakerest';
2
3const adapter = new MswAdapter({ baseUrl: 'http://my.custom.domain', data });
4const authorsCollection = new Collection({ items: [], identifierName: '_id' });
5adapter.server.addCollection('authors', authorsCollection);
6const handler = adapter.getHandler();
By default, FakeRest uses an auto-incremented sequence for the item identifiers. If you'd rather use UUIDs for instance but would like to avoid providing them when you insert new items, you can provide your own function:
1import { getMswHandler } from 'fakerest'; 2import uuid from 'uuid'; 3 4const handler = new getMswHandler({ 5 baseUrl: 'http://my.custom.domain', 6 data, 7 getNewId: () => uuid.v5() 8});
You can also specify that on a per-collection basis:
1import { MswAdapter, Collection } from 'fakerest';
2import uuid from 'uuid';
3
4const adapter = new MswAdapter({ baseUrl: 'http://my.custom.domain', data });
5const authorsCollection = new Collection({ items: [], getNewId: () => uuid.v5() });
6adapter.server.addCollection('authors', authorsCollection);
7const handler = adapter.getHandler();
Some APIs might enforce some parameters on queries. For instance, an API might always include an embed or enforce a query filter.
You can simulate this using the defaultQuery
parameter:
1import { getMswHandler } from 'fakerest'; 2import uuid from 'uuid'; 3 4const handler = getMswHandler({ 5 baseUrl: 'http://my.custom.domain', 6 data, 7 defaultQuery: (collection) => { 8 if (resourceName == 'authors') return { embed: ['books'] } 9 if (resourceName == 'books') return { filter: { published: true } } 10 return {}; 11 } 12});
Behind a simple API (getXXXHandler
), FakeRest uses a modular architecture that lets you combine different components to build a fake REST server that fits your needs.
getXXXHandler
is a shortcut to an object-oriented API of adapter classes:
1export const getMswHandler = (options: MswAdapterOptions) => { 2 const server = new MswAdapter(options); 3 return server.getHandler(); 4};
FakeRest provides 3 adapter classes:
MswAdapter
: Based on MSWFetchMockAdapter
: Based on fetch-mock
SinonAdapter
: Based on SinonYou can use the adapter class directly, e.g. if you want to make the adapter instance available in the global scope for debugging purposes:
1import { MsWAdapter } from 'fakerest'; 2 3const adapter = new MswAdapter({ 4 baseUrl: 'http://my.custom.domain', 5 data: { 6 'authors': [ 7 { id: 0, first_name: 'Leo', last_name: 'Tolstoi' }, 8 { id: 1, first_name: 'Jane', last_name: 'Austen' } 9 ], 10 'books': [ 11 { id: 0, author_id: 0, title: 'Anna Karenina' }, 12 { id: 1, author_id: 0, title: 'War and Peace' }, 13 { id: 2, author_id: 1, title: 'Pride and Prejudice' }, 14 { id: 3, author_id: 1, title: 'Sense and Sensibility' } 15 ], 16 'settings': { 17 language: 'english', 18 preferred_format: 'hardback', 19 } 20 } 21}); 22window.fakerest = adapter; 23const handler = adapter.getHandler();
Adapters transform requests to a normalized format, pass them to a server object, and transform the normalized server response into the format expected by the mocking library.
The server object implements the REST syntax. It takes a normalized request and exposes a handle
method that returns a normalized response. FakeRest currently provides only one server implementation: SimpleRestServer
.
You can specify the server to use in an adapter by passing the server
option:
1const server = new SimpleRestServer({ 2 baseUrl: 'http://my.custom.domain', 3 data: { 4 'authors': [ 5 { id: 0, first_name: 'Leo', last_name: 'Tolstoi' }, 6 { id: 1, first_name: 'Jane', last_name: 'Austen' } 7 ], 8 'books': [ 9 { id: 0, author_id: 0, title: 'Anna Karenina' }, 10 { id: 1, author_id: 0, title: 'War and Peace' }, 11 { id: 2, author_id: 1, title: 'Pride and Prejudice' }, 12 { id: 3, author_id: 1, title: 'Sense and Sensibility' } 13 ], 14 'settings': { 15 language: 'english', 16 preferred_format: 'hardback', 17 } 18 } 19}); 20const adapter = new MswAdapter({ server }); 21const handler = adapter.getHandler();
You can provide an alternative server implementation. This class must implement the APIServer
type:
1export type APIServer = { 2 baseUrl?: string; 3 handle: (context: FakeRestContext) => Promise<BaseResponse>; 4}; 5 6export type BaseResponse = { 7 status: number; 8 body?: Record<string, any> | Record<string, any>[]; 9 headers: { [key: string]: string }; 10}; 11 12export type FakeRestContext = { 13 url?: string; 14 headers?: Headers; 15 method?: string; 16 collection?: string; 17 single?: string; 18 requestBody: Record<string, any> | undefined; 19 params: { [key: string]: any }; 20};
The FakerRestContext
type describes the normalized request. It's usually the adapter's job to transform the request from the mocking library to this format.
The querying logic is implemented in a class called Database
, which is independent of the server. It contains collections and single.
You can specify the database used by a server by setting its database
property:
1const database = new Database({ 2 data: { 3 'authors': [ 4 { id: 0, first_name: 'Leo', last_name: 'Tolstoi' }, 5 { id: 1, first_name: 'Jane', last_name: 'Austen' } 6 ], 7 'books': [ 8 { id: 0, author_id: 0, title: 'Anna Karenina' }, 9 { id: 1, author_id: 0, title: 'War and Peace' }, 10 { id: 2, author_id: 1, title: 'Pride and Prejudice' }, 11 { id: 3, author_id: 1, title: 'Sense and Sensibility' } 12 ], 13 'settings': { 14 language: 'english', 15 preferred_format: 'hardback', 16 } 17 } 18}); 19const server = new SimpleRestServer({ baseUrl: 'http://my.custom.domain', database });
You can even use the database object if you want to manipulate the data:
1database.updateOne('authors', 0, { first_name: 'Lev' });
The Database may contain collections and singles. In the following example, authors
and books
are collections, and settings
is a single.
1const handler = getMswHandler({ 2 baseUrl: 'http://localhost:3000', 3 data: { 4 'authors': [ 5 { id: 0, first_name: 'Leo', last_name: 'Tolstoi' }, 6 { id: 1, first_name: 'Jane', last_name: 'Austen' } 7 ], 8 'books': [ 9 { id: 0, author_id: 0, title: 'Anna Karenina' }, 10 { id: 1, author_id: 0, title: 'War and Peace' }, 11 { id: 2, author_id: 1, title: 'Pride and Prejudice' }, 12 { id: 3, author_id: 1, title: 'Sense and Sensibility' } 13 ], 14 'settings': { 15 language: 'english', 16 preferred_format: 'hardback', 17 } 18 } 19});
A collection is the equivalent of a classic database table. It supports filtering and direct access to records by their identifier.
A single represents an API endpoint that returns a single entity. It's useful for things such as user profile routes (/me
) or global settings (/settings
).
FakeRest supports embedding other resources in a main resource query result. For instance, embedding the author of a book.
GET /books/2?embed=['author']
HTTP 1.1 200 OK
Content-Type: application/json
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } }
Embeds are defined by the query, they require no setup in the database.
1# Install dependencies 2make install 3 4# Run the demo with MSW 5make run-msw 6 7# Run the demo with fetch-mock 8make run-fetch-mock 9 10# Run the demo with sinon 11make run-sinon 12 13# Run tests 14make test 15 16# Build minified version 17make build
You can sign-in to the demo with janedoe
and password
FakeRest is licensed under the MIT License, sponsored by marmelab.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
license file detected
Details
Reason
10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
Reason
SAST tool is not run on all commits -- score normalized to 7
Details
Reason
4 existing vulnerabilities detected
Details
Reason
Found 4/15 approved changesets -- score normalized to 2
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Score
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More