Move all validation schema into lib/validate

This commit is contained in:
ekzyis 2024-07-04 22:42:14 +02:00
parent 39d8928772
commit b96757b366
4 changed files with 74 additions and 69 deletions

View File

@ -1,5 +1,4 @@
import { TOR_REGEXP } from '@/lib/url'
import { object, string } from 'yup'
import { LNbitsSchema } from '@/lib/validate'
export const name = 'lnbits'
@ -28,30 +27,7 @@ export async function validate ({ logger, url, adminKey }) {
logger.ok('wallet found')
}
export const schema = object({
url: process.env.NODE_ENV === 'development'
? string()
.or([string().matches(/^(http:\/\/)?localhost:\d+$/), string().url()], 'invalid url')
.required('required').trim()
: string().url().required('required').trim()
.test(async (url, context) => {
if (TOR_REGEXP.test(url)) {
// allow HTTP and HTTPS over Tor
if (!/^https?:\/\//.test(url)) {
return context.createError({ message: 'http or https required' })
}
return true
}
try {
// force HTTPS over clearnet
await string().https().validate(url)
} catch (err) {
return context.createError({ message: err.message })
}
return true
}),
adminKey: string().length(32)
})
export const schema = LNbitsSchema
export async function sendPayment ({ bolt11, url, adminKey }) {
const response = await postPayment(url, adminKey, bolt11)

View File

@ -1,12 +1,11 @@
import bip39Words from '@/lib/bip39-words'
import LNC from '@lightninglabs/lnc-web'
import { Mutex } from 'async-mutex'
import { string, array, object } from 'yup'
import { Form, PasswordInput, SubmitButton } from '@/components/form'
import CancelButton from '@/components/cancel-button'
import { InvoiceCanceledError, InvoiceExpiredError } from '@/components/payment'
import { bolt11Tags } from '@/lib/bolt11'
import { Status } from '@/components/wallet'
import { LNCSchema } from '@/lib/validate'
export const name = 'lnc'
@ -58,20 +57,7 @@ export async function validate ({ me, logger, pairingPhrase, password }) {
}
}
export const schema = object({
pairingPhrase: array()
.transform(function (value, originalValue) {
if (this.isType(value) && value !== null) {
return value
}
return originalValue ? originalValue.split(/[\s]+/) : []
})
.of(string().trim().oneOf(bip39Words, ({ value }) => `'${value}' is not a valid pairing phrase word`))
.min(2, 'needs at least two words')
.max(10, 'max 10 words')
.required('required'),
password: string()
})
export const schema = LNCSchema
const mutex = new Mutex()

View File

@ -1,7 +1,6 @@
import { NOSTR_PUBKEY_HEX } from '@/lib/nostr'
import { parseNwcUrl } from '@/lib/url'
import { NWCSchema } from '@/lib/validate'
import { Relay, finalizeEvent, nip04 } from 'nostr-tools'
import { object, string } from 'yup'
export const name = 'nwc'
@ -19,31 +18,7 @@ export const card = {
badges: ['send only', 'non-custodialish']
}
export const schema = object({
nwcUrl: string()
.required('required')
.test(async (nwcUrl, context) => {
// run validation in sequence to control order of errors
// inspired by https://github.com/jquense/yup/issues/851#issuecomment-1049705180
try {
await string().required('required').validate(nwcUrl)
await string().matches(/^nostr\+?walletconnect:\/\//, { message: 'must start with nostr+walletconnect://' }).validate(nwcUrl)
let relayUrl, walletPubkey, secret
try {
({ relayUrl, walletPubkey, secret } = parseNwcUrl(nwcUrl))
} catch {
// invalid URL error. handle as if pubkey validation failed to not confuse user.
throw new Error('pubkey must be 64 hex chars')
}
await string().required('pubkey required').trim().matches(NOSTR_PUBKEY_HEX, 'pubkey must be 64 hex chars').validate(walletPubkey)
await string().required('relay url required').trim().wss('relay must use wss://').validate(relayUrl)
await string().required('secret required').trim().matches(/^[0-9a-fA-F]{64}$/, 'secret must be 64 hex chars').validate(secret)
} catch (err) {
return context.createError({ message: err.message })
}
return true
})
})
export const schema = NWCSchema
export async function validate ({ logger, nwcUrl }) {
const { relayUrl, walletPubkey } = parseNwcUrl(nwcUrl)

View File

@ -12,6 +12,8 @@ import * as subsFragments from '@/fragments/subs'
import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon'
import { datePivot } from './time'
import { decodeRune } from '@/lib/cln'
import { TOR_REGEXP, parseNwcUrl } from '@/lib/url'
import bip39Words from '@/lib/bip39-words'
const { SUB } = subsFragments
const { NAME_QUERY } = usersFragments
@ -303,6 +305,72 @@ export function advSchema (args) {
})
}
export const LNbitsSchema = object({
url: process.env.NODE_ENV === 'development'
? string()
.or([string().matches(/^(http:\/\/)?localhost:\d+$/), string().url()], 'invalid url')
.required('required').trim()
: string().url().required('required').trim()
.test(async (url, context) => {
if (TOR_REGEXP.test(url)) {
// allow HTTP and HTTPS over Tor
if (!/^https?:\/\//.test(url)) {
return context.createError({ message: 'http or https required' })
}
return true
}
try {
// force HTTPS over clearnet
await string().https().validate(url)
} catch (err) {
return context.createError({ message: err.message })
}
return true
}),
adminKey: string().length(32)
})
export const NWCSchema = object({
nwcUrl: string()
.required('required')
.test(async (nwcUrl, context) => {
// run validation in sequence to control order of errors
// inspired by https://github.com/jquense/yup/issues/851#issuecomment-1049705180
try {
await string().required('required').validate(nwcUrl)
await string().matches(/^nostr\+?walletconnect:\/\//, { message: 'must start with nostr+walletconnect://' }).validate(nwcUrl)
let relayUrl, walletPubkey, secret
try {
({ relayUrl, walletPubkey, secret } = parseNwcUrl(nwcUrl))
} catch {
// invalid URL error. handle as if pubkey validation failed to not confuse user.
throw new Error('pubkey must be 64 hex chars')
}
await string().required('pubkey required').trim().matches(NOSTR_PUBKEY_HEX, 'pubkey must be 64 hex chars').validate(walletPubkey)
await string().required('relay url required').trim().wss('relay must use wss://').validate(relayUrl)
await string().required('secret required').trim().matches(/^[0-9a-fA-F]{64}$/, 'secret must be 64 hex chars').validate(secret)
} catch (err) {
return context.createError({ message: err.message })
}
return true
})
})
export const LNCSchema = object({
pairingPhrase: array()
.transform(function (value, originalValue) {
if (this.isType(value) && value !== null) {
return value
}
return originalValue ? originalValue.split(/[\s]+/) : []
})
.of(string().trim().oneOf(bip39Words, ({ value }) => `'${value}' is not a valid pairing phrase word`))
.min(2, 'needs at least two words')
.max(10, 'max 10 words')
.required('required'),
password: string()
})
export function lnAddrAutowithdrawSchema ({ me } = {}) {
return object({
address: lightningAddressValidator.required('required').test({