Tips for working with historical data in Sanity and how to sanitize user input
17 replies
Last updated: May 20, 2021
D
Anyone here worked with historical data in Sanity? We want to give our users a way to navigate historical versions of our pages, so the plan is to use the history-api to do some clever queries and display a dropdown menu where the users can select different revisions of the page. If anyone has done something similar I would greatly appreciate some tips and pointers 🙂 For example it doesn't seem like it possible to use the history-api from the
@sanity/client?
May 6, 2021, 7:49 AM
Hi Daniel! You’re right that our js-client don’t have support for the history API. This is how the Studio are fetching the history https://github.com/sanity-io/sanity/blob/next/packages/@sanity/desk-tool/src/panes/documentPane/documentHistory/history/controller.ts#L256
May 6, 2021, 8:16 AM
D
Thanks
user P
, thats very helpfull 🙌May 6, 2021, 8:32 AM
D
We've built a solution where our end users can browse document history, and it works really nicely thanks to the history-api 🎉
Right now we are fetching document-history server-side (nextjs) using urls built like this: ``https://${projectId}.
apicdn.sanity.io/v1/data/history/${dataset}/documents/${docId}?time=${time} `` .). Does this leave us open to some kind of vulnerability? Should we for example sanitise this input, validate it, cache it, or worry about ddos attacks etc..? Other things we should consider?
Thankful for any input on this
🙂
Code here:
https://github.com/navikt/dp-faktasider-frontend/blob/08795ca914ae4f649c419b04e14bc832e9b1f5a1/src/pages/historikk/%5B...slug%5D.tsx#L34
https://github.com/navikt/dp-faktasider-frontend/blob/08795ca914ae4f649c419b04e14bc832e9b1f5a1/src/components/historikk/api/historikkFetcher.ts#L18
https://github.com/navikt/dp-faktasider-frontend/blob/08795ca914ae4f649c419b04e14bc832e9b1f5a1/src/components/historikk/api/revisionsFetcher.ts#L16
Right now we are fetching document-history server-side (nextjs) using urls built like this: ``https://${projectId}.
apicdn.sanity.io/v1/data/history/${dataset}/documents/${docId}?time=${time} `` .
timeand
docIdis taken directly from the request-url (for example
<http://localhost:3000/historikk/0caeeb00-deb0-4310-bb04-17ffb2ccd165/2021-05-12T12:09:23.000Z>
Thankful for any input on this
🙂
Code here:
https://github.com/navikt/dp-faktasider-frontend/blob/08795ca914ae4f649c419b04e14bc832e9b1f5a1/src/pages/historikk/%5B...slug%5D.tsx#L34
https://github.com/navikt/dp-faktasider-frontend/blob/08795ca914ae4f649c419b04e14bc832e9b1f5a1/src/components/historikk/api/historikkFetcher.ts#L18
https://github.com/navikt/dp-faktasider-frontend/blob/08795ca914ae4f649c419b04e14bc832e9b1f5a1/src/components/historikk/api/revisionsFetcher.ts#L16
May 19, 2021, 11:53 AM
Hi
I would always sanitise user input. If you’re taking the docId and time directly without sanitising it it might leave you vulnerable to path traversal attacks. Without having looked to much on the code, and I assume the
user Q
!I would always sanitise user input. If you’re taking the docId and time directly without sanitising it it might leave you vulnerable to path traversal attacks. Without having looked to much on the code, and I assume the
historikkFetcheris using a client/token with elevated privileges, it could be possible to construct a query that fetches a document you shouldn’t have access to
May 19, 2021, 3:40 PM
D
Thanks
I'm new to sanitising user input, what is the correct way to do this? Any libraries/javascript functionality that can sanitise this in a meaningful way? Or checking the id with a regex like
Any other threats we should consider? By allowing any time as input, are we more vulnerable against ddos-attacks for example?
user P
🙂I'm new to sanitising user input, what is the correct way to do this? Any libraries/javascript functionality that can sanitise this in a meaningful way? Or checking the id with a regex like
/[^a-z0-9-]/.test(docId)and time with another suitable regex?
Any other threats we should consider? By allowing any time as input, are we more vulnerable against ddos-attacks for example?
May 20, 2021, 8:29 AM
D
Any suggestion as to how we should sanitise these inputs? Any libraries that can sanitise this in a meaningful way? Or checking the id with a regex like
Any other threats we should consider? By allowing any time as input, are we more vulnerable against ddos-attacks for example?
/[^a-z0-9-]/.test(docId)and time with another suitable regex?
Any other threats we should consider? By allowing any time as input, are we more vulnerable against ddos-attacks for example?
May 20, 2021, 8:29 AM
/[^a-z0-9-]/.test(docId)will match everything that isn’t:
a-z,
0-9or
-, I assume you maybe meant without the negative op(
^) ?
May 20, 2021, 11:22 AM
D
These are Sanity-document IDs, so I thought I'd test if they used some illegal characters and in that case throw an error or something 🙂 But would maybe a simple
encodeURIComponent(id/time)do the trick?
May 20, 2021, 11:28 AM
Right! The id’s generated by the studio are uuids, would return true on a valid id
const uuidMatcher = /^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+$/
May 20, 2021, 11:33 AM
with
encodeURIComponentyou should decode on receiving end, it’s not for sanitising input in that way.ie,
encodeURIComponent("+") => %2Bso if the
timefield contains a plus it won’t be valid when you pass it to the api client 🙂
May 20, 2021, 11:35 AM
with
encodeURIComponentyou should decode on receiving end, it’s not for sanitising input in that way.ie,
encodeURIComponent("+") => %2Bso if the
timefield contains a plus it won’t be valid when you pass it to the api client 🙂
May 20, 2021, 11:35 AM
https://date-fns.org/v2.21.3/docs/isValid Can be used for validating the time field, or you can do
function isValidDate(time) { try { new Date(time); return true } catch(err) { return false; }}
May 20, 2021, 11:40 AM
D
Perfect, thank you 🙂 🙌
May 20, 2021, 11:41 AM
Sanity– build remarkable experiences at scale
Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.