コフス技術ブログ

Ajvをスタンドアロン化しCSP環境下で利用する流れ

この記事はにメンテナンスが行われています。

JSON schemaに対応したバリデーターの1つにAjvがあります。
JavaScriptでjson形式のバリデーションを行う場面でよく使われますが、CSP(コンテンツセキュリティポリシー)制限がある環境下では以下の様なエラーで実行が拒否されます。

EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' blob: filesystem:".

これはAjvがschemaをコンパイルする際にnew Function()を行うからであり、これがCSP制限に引っかかりスクリプトの実行そのものが拒否される為です。

The content security policy for Chrome Apps restricts you from doing the following:

You can't use inline scripting in your Chrome App pages. The restriction bans both <script> blocks and event handlers (<button onclick="...">).
You can't reference any external resources in any of your app files (except for video and audio resources). You can't embed external resources in an iframe.
You can't use string-to-JavaScript methods like eval() and new Function().

引用:Content Security Policy

デフォルトでCSP制限に上手に対応しているバリデーターもありますが、AjvもCSP制限をサポートしており、スタンドアロン化することでCSP環境下でも使用することが可能です。

Ajvのスタンドアロン化

Ajvのissuesでは上記内容と同等のCSP制限に関した質問がいくつか見られるようで、初見だと詰まる方もいるかもしれませんが特段難しい作業が必要という訳ではありません。

要はコンパイルの際に行うnew Function()がCSP環境下で制限に引っかかるのであれば、事前にコンパイル済みのスタンドアロン版を用意してしまえば良いという事です。

スタンドアロン化はCLI版のajv-cliを用いて生成が可能です。

インストール

グローバルにajv-cliをインストールします。

npm install -g ajv-cli

スタンドアロン化

JSON schemaの内容を記述したファイルをschema.jsonとして用意します。

例)schema.jsonの記述例

schema.json
{
  "type": "object",
  "properties": {
    "foo": {"type": "integer"},
    "bar": {"type": "string"}
  },
  "required": ["foo"],
  "additionalProperties": false
}

以下でコンパイルを行います。

ajv compile -s schema.json -o validate-schema.js

出力されたvalidate-schema.jsがスタンドアロン化されたバリデーターになります。

使用方法

一般的なAjvの使用方法が以下ですが、スタンドアロン版も基本的には同じ流れで使用可能です。

normal-ajv.js
import Ajv from "ajv"

const ajv = new Ajv()

const schema = {
  type: "object",
  properties: {
    foo: {type: "integer"},
    bar: {type: "string"}
  },
  required: ["foo"],
  additionalProperties: false,
}

const validate = ajv.compile(schema)
const valid = validate(data)
if (!valid) console.log(validate.errors)

以下がスタンドアロン版を使用した記述です。
すでにコンパイル済みなので記述は少なく非常にスッキリしています。

standalone-ajv.js
import standaloneValidate from 'validate-schema'

const valid = standaloneValidate(data)
if (!valid) console.log(validate.errors)

スタンドアロン化したvalidate-schema.jsをimportし、バリデーションを行うjsonデータを引数に渡してあげるだけです。


スタンドアロン化はCLI以外にajv/dist/standaloneを読み込んだコード上でも行うことができます。頻繁にJSON schemaが変わる場合はfsで更新するのも良さそうです。

参考:Standalone validation code - Usage from code