Skip to content

v0.6.24 — JSON の項目名と JavaScript の継承を、分けていませんでした

Latest

Choose a tag to compare

@Driedsandwich Driedsandwich released this 16 Aug 08:53
· 2 commits to main since this release

v0.6.24 — JSON の項目名と JavaScript の継承を、分けていませんでした

⚠ 先に読むところ

1. 根は 2 つ。指摘は構文 4 つで、こちらは実箇所を 6 つ数えました

版を上げるかどうかを決める判定器(scripts/schemaLanguageDiff.mjs)に、
外部監査から P0 1 件・P1 1 件の指摘がありました。どちらもそのまま再現しました。

根A。 {}toStringconstructor継承しています

'toString' in {}          → true    (own property は無いのに)
({})['toString']          → 関数     (undefined ではない)
out['__proto__'] = x      → own property にならない(prototype が差し替わる)

properties の key は受け手が決める名前なので toString も来ます。
判定器はそこを分けずに集合差を取っていたので、項目の追加が見えませんでした。

旧  { type:'object', additionalProperties:false, properties:{} }
新  { …, properties:{ toString:{ type:'string' } } }

ajv  {"toString":"x"} が旧 invalid → 新 valid  = **広がっている**
判定 HOLD / exit 0                            ← 危険側

根B。 正当な再帰 schema(linked list)を比べると
RangeError: Maximum call stack size exceeded判定を返せませんでした。
打ち切りの鍵が pointer 文字列で、/properties/next/properties/next/…
伸び続けるため、同じ節へ戻っても一度も重複しません(実測: 8 段たどって重複 0 件)。

**根B は危険側ではありません。**誤った HOLD を返すのではなく、
答えを返せない——版を決める門が使えなくなる形です。だから直し方も違います。

指摘は危険な構文を 4 つ名指ししていました。直す前に実箇所を数えたところ、
根A だけで 5 か所ありました。

A1a / A1b / A1c  properties の集合差 3 か所   ← toString の反例はここ
A2               expandRefs の accumulator が {}   ← __proto__ が own にならない
A3               op[k] / np[k] で引く
A4               deref の traversal
A5               resolvePointer の traversal
B                打ち切りの鍵が pointer 文字列

hasOwn / own / bag(=Object.create(null))を 1 か所に置き、全部そこへ通しました。

2. __proto__ だけは ajv でも真値が出せません

指摘の 2 件目は oneOf の枝の中で __proto__ の型を広げる形でした。
**こちらでは ajv が広がりを認めません。**対照行列で切り分けました。

toString / constructor / hasOwnProperty / valueOf   → ajv は own property として正しく通す
__proto__                                           → **ajv 自身が新旧とも拒否**

正解器が同じ盲点を持っています。{"__proto__":"x"} を JSON.parse で作れば
own property になりますが、ajv の内部がそれを own key として扱えません。

だからこの 1 件は判定器の側だけを固定しました(条文 ①-c13 ⑦)。
「ajv が真値を出せる」という前提が崩れた初めての例です。

3. 変異対照で 3 か所が「外しても落ちない行」でした

回帰試験を先に入れ(条文 ①-c13 / ①-c14)、修正前に 5 件落ちることを実測しました。
そのあと直した箇所を 1 つずつ戻したところ、3 か所は消しても全件通りました。

実害を先に測ってから、3 つに分けています。

A1c 共通集合        **精度**       戻すと同じ項目を 2 回数え、正しくは HOLD_RECORD の
                                削除が **誤って BUMP** になる
A5 resolvePointer  **正しさ**     判定は変わらない。contractMigration の
                                「その pointer は旧に在ったか」が**継承した関数を拾って true**
A3 op[k] / np[k]   **falsify 不能** `k` は `Object.keys(op)` から来るので**定義上いつも own**。
                                どの形でも判定が変わらない

**A3 は消しても落ちません。**承知のうえで残しました——A1c が将来戻ったときに効きます。
コードとテストの両方にその申告を書いています。

足したあと、A3 以外の 7 か所+生成器 3 か所が変異で落ちます。

4. P1 の最初の直し方が間違っていました

「一度でも見た節の組」を覚える形にしたところ、別の試験が 1 件落ちました
——contractMigration の記録した差分が実物から消えていたのです
/properties/nominalConfiguration/properties/windows/items)。

同じ節が 2 つの経路から来るとき、2 回目を飛ばしてその経路の差分を出さなかったからです。

「一度でも見たか」  → 別の経路から来た同じ節の結果まで消える
「いま辿っている経路上にあるか」  → 循環だけを止める(正しい)

seenスタックとして使う形へ直しました——降りるときに入れ、戻るときに外す
変異は 2 方向(鍵を pointer へ戻す/戻るときに外さない)とも落ちます。

出力が減るだけの欠陥は、落ちる試験が無ければ気づけません。
このときは既存の試験が拾ってくれました。

5. 現行 schema にこの 2 形は 1 件もありません

properties の key が prototype 名   0 件
循環する $ref                      0 件
(現行 26 schema を走査)

実 schema 200 対(現行 26 schema × 直近 8 tag)で判定は 1 件も変わっていません。
この版の公開物への影響はありません。直したのは将来の版を決める道具です。

**試験の側も直しました。**v0.6.23 の生成器は
prototype 名の property を 0 件、再帰する $ref0 件しか作っていません。
補強後は 9 件 / 42 件です。

版数を上げていない根拠(すべて実測)

toolVersion 20 / CLI 結果契約 schemaVersion 2 / profile schemaVersion 3 のままです。

判定器は配布物か        **違う**(releaseAssets に 0 回。対照: 検算ツールは 1 回 / 配布物 29 件)
配る検算ツール          v0.6.23 と **byte 一致**
                        da1e5558d0a4cdf38c23274c4486bbbbf94cc79bf02a28a49cd01765af325401
実 schema での判定       **200 対中 0 件が変化**(現行 26 schema × 直近 8 tag)
CLI 出力の 10 経路       基準と byte 一致
profile のモデル数値     **0 件が変化**(葉 2267 / 3130 のうち、動いたのは由来 8 件だけ)

⚠️ 「何も変わらない」ではありません。§1 の 6 か所では判定が変わります。
**それが今回直した欠陥です。**現行 schema にその形が無いので、
実 schema での判定が変わらなかっただけです。

まだやっていないこと

  • **実測が 1 件も来ていません。**接点位置は全部仮定のままで、
    導通測定も音響測定もしていません(verifiedPhysicalfalse)。
  • annotated tag への署名(v0.6.24 も署名なしです)。
  • main の branch protection / ruleset。監査の P2 ですが、
    リポジトリ設定の変更なので手を付けていません。
  • Half-Plug 下流の release-lock v7 / mechanism-binding v7(別 repo)。
  • nanoid 3.3.16 の更新。踏む経路が無いと測ったまま据え置きです(SECURITY.md)。
  • BusyBox を oracle に足すこと(開発機にも CI にもありません)。
  • A3 は falsify できないまま残しています(§3)。
  • property-based 試験が見ているのは、いまも 1 つの性質だけです。
    加えて §2 のとおり、正解器(ajv)にも盲点があることが分かりました。

数えたもの

v0.6.23 v0.6.24
単体テスト 1457 1474(skip 0 / 失敗 0)
配る asset 31 変わらず 31
schema 26 変わらず 26
検算ツール toolVersion 20 変わらず 20(byte 一致)
CLI 結果契約 schemaVersion 2 変わらず 2
profile 契約 schemaVersion 3 変わらず 3
止め方の名前(catalog) 82 種類 変わらず 82
出力の基準(経路) 10 変わらず 10(全件 byte 一致)
検証対象(validate:profiles 14 14
記録された入力 32 32
区間 / event TRS 23/32・TRRS 30/36 変わらず

変わっていないもの

  • **モデルの数値。**接点位置も走査結果も同じです。
  • verifiedPhysicalfalse。実測記録は依然 0 件です。
  • IV02813.3–13.52 mm / ASSUMPTION / ground-open-differential /
    RETURN_OPEN_L_AND_R_ON_DISTINCT_CONDUCTORS のままです。
  • toolVersion 20 / CLI 結果契約 schemaVersion 2 / profile schemaVersion 3 / catalog 82 種類。
  • 止め方の名前の一覧・archivePolicy・status・終了コードの意味。
  • 公開済みの release 本文と asset は書き換えていません。
    過去 875 件(v0.1.0〜v0.6.23)が byte 一致であることを、再生成の段ごとに実測しました
    (対照として基準を 1 行壊すと 874 件へ落ち、v0.1.0 の該当ファイルを名指しします)。