
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
@button-inc/form-schema
Advanced tools
Contents
This library is built on top of react json schema form, with an aim to make it easier to create forms that have the following properties:
Note that while this package makes it easier to meet these three goals, it is not limited to them. For example, it can be used with a client-side rendering framework like create react app if you are interested only in accessibility and single-page form questions.
Install package:
npm i @button-inc/form-schema
or yarn add @button-inc/form-schema
depending on your package manager.Add to your app:
import builder from @button-inc/form-schema
, or to use with themed components,
import {govBuilder} from @button-inc/form-schema
See below for specific use cases, or browse our examples for a working setup.
This package is setup to split a json schema into a series of pages, as well as handle redirection, form data, and front-end/back-end validation for users with or without javascript. To do this, it requires three main configuration pieces:
GET
route is required to handle rendering the split-up forms.POST
route is required to handle saving data for the split-up forms.The Form components, functions and middleware to set this up will be returned from the main builder
function (default import). E.g:
import builder from '@button-inc/form-schema';
export const { postMiddleware, getHandler, Forms } = builder(schema, uiSchema, options);
Where the Forms
will be an array of form components.
Since this library utilizes react json schema form, the schema configuration is largely the same. The differences are:
form-schema:
type: 'object'
. They will be shown together on a single page. E.g {
...
properties: {
groupedFields: {
type: 'object',
properties: {
onPageOne: {
type: 'string',
...
},
alsoOnPageOne: {
type: 'string',
...
}
}
}
}
}
oneOf
is supported. anyOf
will be added in the future.multipart
encoding, you can add a custom hasFiles
boolean property to fields of type string
. See files for more information.name: string
.ui-schema: The ui schema has the same properties as react json schema form.
To handle rendering forms, you will need to know any previous form data to load into the form, as well as the index of the current form to render. The getHandler returns these for you, as well as whether or not the current route corresponds to an actual form-page. You can make a request to this route, and use the returned props to render the correct form. For example:
app.get('/getRoute/:id', (req, res) => {
const { formIndex, formData, validPage } = getHandler(req);
res.json({ formIndex, formData, validPage });
});
Note that this is if you want to use sessions to store data. To handle data in between pages yourself, see the custom data handling section.
// Import the array of form components from the file you created them in.
import { Forms } from 'form-schema';
export default function Form({ formIndex, formData, validPage }) {
const Form = Forms[formIndex];
return <>{validPage && <Form formData={formData} />}</>;
}
The Form
component will be an rjsf form component, and can be passed in the usual props, such as templates. In addition, it can take a rerouteHandler prop, which is a function to handle redirection when javascript is enabled. This allows you to use whatever SPA front-end router you need. E.g:
form.jsx
...
const rerouteHandler = (nextPage: string, _isValid: boolean, lastPage: boolean) => {
// Re-route using your routing library
router.push(lastPage ? '/end' : nextPage);
};
return <>{validPage && <Form formData={formData} rerouteHandler={rerouteHandler} />}</>;
}
Note: If not providing this prop, redirection will be handled via window.location
which will cause a re-render.
To save data as the user progresses through pages, a dynamic post route needs to be setup. This post route requires body parsing middleware to be enabled, which is enabled by default in express
(4.16+) and nextjs
. In the dynamic route, simply pass the middleware in. E.g:
app.post('/postRoute/:num', postMiddleware);
If the useSession
option is set as true, you should setup session middleware for this route, and it will parse and save the data for you. If not using sessions, see the custom data handling section which allows you to save the data how you want in between forms. Lastly, when a form is completed a final onFormEnd function will fire, allowing you to see if it validated correctly on the backend, and save the data. This is configured in the options
argument of the builder
function.
For handling file uploads, the data needs to be parsed differently. To support this, a separate POST
route needs to be configured ar postRoute/:num/file
, where postRoute
is the route configured in options. This route can be configured similarily to the base route:
postRoute/:num/file.js
app.post('/postRoute/:num', fileMiddleware);
Inside this route, the body-parser middleware needs to be disabled to allow file parsing. To handle the file stream, the options
configuration passes into builder takes two functions, handleReadStream and onFileLoad. These can be used to pipe the stream onward, and notify when the upload is complete.
By default, a session can be used to store incomplete form data. To handle the data yourself, pass in useSessions: false
to the options configuration, along with an onPost function. The onPost
function can be used to clean the form data and save it in between pages. E.g:
options
onPost: (postData: object, schemaIndex: number, cleanSchemaData: Function) => {
const formData = getDataFromDB();
const newData = cleanSchemaData(formData);
saveDataToDB(newData);
return newData;
},
Note that the cleaned data is expected to be returned by this function.
To retrieve the data, in your GET
route, simply retrieve it after calling the handler.
getRoute.js
app.get('/getRoute/:id', (req, res) => {
const { formIndex, validPage } = getHandler(req);
const formData = getDataFromDB(formIndex);
res.json({ formIndex, formData, validPage });
});
Builder function is the default export of the package, and can also be imported with certain widgets pre-attached by importing the named import govBuilder. For example:
import { govBuilder } from '@button-inc/form-schema';
or
import builder from '@button-inc/form-schema';
It takes the following arguments:
Arguments | Type | Description |
---|---|---|
Schema | Object |
A form-schema compatible with
react json schema form
. In addition to the base configuration options, any field under the properties key can be passed:
object . All of it's child properties will be grouped together.
|
uiSchema | object | A ui schema compatible with react json schema form |
options | object |
A configuration object. It can be passed the following keys:
|
Builder returns the values:
A function to generate custom validations. It takes the following arguments:
Arguments | Type | Description |
---|---|---|
fieldValue | string | The value of the field being validated |
formData | object | All data entered for the current application. |
The return value should be a boolean indicating whether or not the input is valid.
A function to handle file upload streams. It taks arguments:
Argument | Type | Description |
---|---|---|
filename | string | The name of the uploaded file. |
readStream | Readable Stream | A readable stream of the uploaded file. |
A callback that will fire when files finish uploading. It takes the arguments:
Argument | Type | Description |
---|---|---|
filename | string | The name of the uploaded file. |
A callback that will fire when form data is posted to the API. It takes the arguments:
Argument | Type | Description |
---|---|---|
postedData | object | The data that was posted by the user. |
schemaIndex | number | Index of the schema that was posted from. |
cleanSchemaData | function | Function to clean any form data you are using. This will remove fields for the current form page from the data, so that new user data will still be updated for fields such as un-checked checkboxes, which don't post anything in the non-javascript use case. It takes the current formdata as an argument, e.g
const formData = getFormDataFromDB(); const newData = cleanSchemaData(formData); saveNewDataToDB(newData); |
The return value should be the cleaned data.
A callback that will fire when the form is completes. It takes the arguments:
Argument | Type | Description |
---|---|---|
errors | Array | An array of error objects for any failed fields. |
formData | object | The data of the final submission. |
This function should be used at the dynamic route following the getRoute
you set in options
. For example, if you setup your get route to be '/'
,
you should call getHandler
at '/:num'
. It is used to return the information you need to render the correct form, see the return values below. It takes arguments:
Argument | Type | Description |
---|---|---|
request | object | The request object for the current route. |
The return value for the function is an object with the following properties:
formIndex
: A number showing the index of the form component to render.formData
: An object containing current formData to pass into the form. Intended for use with sessions enabled, if not using sessions will be an empty object.validPage
: A boolean indicating whether or not the current route points to a valid form page.FAQs
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.