Repository navigation
fix(доступ): поле под маской — заполнить пустое, набранное не превращается в звёздочки - #1923
Conversation
Защита «нельзя изменить то, что не видно» при сохранении записанного объекта восстанавливала из базы каждое поле под маской. Пустое значение роль видит пустым — скрывать там нечего, — но и введённое ею молча отбрасывалось. Так оператор, которому телефоны клиента показываются маской, не мог ввести телефон в документ, созданный кодом и открытый для оформления (заявка из звонка): форма записывалась без ошибки, номер терялся. Теперь пустое под маской (mask_*) заполнить можно; заполненное по-прежнему не меняется ни маской, ни подделанным значением; поле hide не заполняется никогда — его пустоту роль не видит. Правило общее для формы, обработчиков формы, DSL и REST (access.MaskedEmptyFillable). Generated-with: Claude Code
Продолжение предыдущего коммита — найдено на живой форме. Ответ события управляемой формы маскировал все защищённые поля, в том числе номер, который пользователь только что набрал: следующее событие («Принять заявку» → ответ на вопрос) или запись присылали на сервер уже «••••••». Раньше номер так молча терялся, а с заполнением пустого поля в базу легли бы сами звёздочки. - значение со знаком маски никогда не заполняет поле (access.LooksMasked): это маска, которую показала форма, а не данные; - ответ события возвращает без маски ровно то, что клиент прислал в этом запросе (он это значение и так знает); значение из базы и поставленное обработчиком уходят маской, как раньше; - поле под маской в управляемой форме помечено data-ob-protected и показывается точками, как пароль (-webkit-text-security). Generated-with: Claude Code
|
Новый HEAD |
Generated-with: Claude Code
…ая маска Найдено на живой форме: в телефоне заявки пропала последняя цифра. Ответ form-event перезаписывал все поля, в том числе то, которое пользователь набирал, пока шёл запрос, — символы, набранные за это время, стирались (с точками вместо цифр этого не видно). Теперь applyValues не трогает поле, изменённое после отправки, если сервер вернул ровно отправленное значение; изменённое сервером применяется как раньше. Точками, как пароль, показывается только поле под mask_all: mask_tail и mask_city намеренно показывают часть значения, точки скрывали и её. Generated-with: Claude Code
|
Новый HEAD |
|
Ревью. (круг 1) |
|
PromptPilot service marker: REVIEW result publication claimed. |
|
PromptPilot service marker: REVIEW result committed. |
Generated-with: Codex PP-Fix-Transition: from=a7b99ef1a462b9aa1b2eb395527660525dfe192e review-comment=6045729526 claim=6045730290 epoch-sha256=03fa7e23300be484c37ace6d55cbdaa15407a51add73e904c5b4b2b04f8881ff
|
Доработан блокирующий пункт 1 завершённого ревью 6045729526. Ответ события, записавшего объект, теперь сбрасывает dirty-флаг только при неизменном editRevision отправленного снимка. Если пользователь допечатал телефон во время запроса, сохранённое на экране значение остаётся несохранённой правкой: закрытие формы требует подтверждения. При отсутствии позднего ввода успешная запись по-прежнему очищает dirty-флаг. Добавлена регрессия полного сценария: публичный POST /ui/catalog/Клиент/form-event с Объект.Записать() действительно сохраняет отправленный номер и отдаёт dirty=false/version; этот HTTP-ответ передаётся клиентскому тесту window.obFire с настоящими applyValues и обработчиками input. Проверены поздний ввод на чистой и уже изменённой форме, подтверждение закрытия и передача полного номера в save-запрос. Контроль без позднего ввода закрывается без лишнего вопроса. На исходном коде оба сценария позднего ввода падали из-за сброса dirty-флага; после правки проходят. Проверено: go build ./...; go test -count=1 -timeout=10m ./internal/ui; go vet ./internal/ui; onebase check --project examples/trade; git diff --check. Все проверки прошли. С Node 22.21.0 восемь клиентских наборов дали 127 успешных тестов без пропусков, включая сохранение события, закрытие, ввод, ссылки, зеркала, реквизиты, чтение файлов и подбор. PostgreSQL для локального прогона не задавался; семантика SQL не менялась. Доработка ограничена блокером ревью; раздел «Хвост» пуст. Новый HEAD 63285e5 опубликован CAS-push с ожидаемым исходным SHA. Удалённая ветка, HEAD PR, единственный родитель и PP-Fix-Transition подтверждены через REST. Обязательный GitHub CI нового SHA запущен и выполняется; успех ещё не подтверждён. Следующий шаг — независимое REVIEW нового HEAD. Generated-with: Codex |
|
Ревью. (круг 2)
|
|
PromptPilot service marker: REVIEW result publication claimed. |
|
PromptPilot service marker: REVIEW result committed. |
Политика hide сохраняет отсутствие поля в ответе даже для значения, присланного клиентом. HTTP-регрессия проверяет hide на заполненном и пустом поле и сохраняет эхо для mask_all, mask_tail и mask_city. Generated-with: Codex PP-Fix-Transition: from=63285e522a5f1b2747e38f4ced1f524bc8fc2386 review-comment=6062113653 claim=6062114622 epoch-sha256=61530e97c3e8dee8bdbfb21ebef490dc7d6db4a9a4ccdf65fc6fb20381bd5486
|
Доработан блокирующий пункт 1 завершённого ревью 6062113653. Поле с политикой hide теперь исключено из эха присланных значений: ответ события не восстанавливает ключ после применения маскирования. Эхо разрешённых mask_all, mask_tail и mask_city сохраняется. Добавлена HTTP-регрессия через s.Mount и POST /ui/catalog/Клиент/form-event с настоящей политикой доступа и пустым обработчиком. Проверены произвольное значение скрытого поля, совпадение с базой и пустое поле в базе; во всех случаях ключ отсутствует в values. На исходном HEAD эти три сценария падали, после исправления проходят. В том же тесте проверено сохранение эха трёх mask_*; существующие проверки заполнения, эха и позднего ввода также прошли. Проверено: go build ./...; go test -count=1 -timeout=10m ./internal/ui с Node 22.21.0 в PATH; go vet ./internal/ui; go run ./cmd/onebase check --project examples/trade; git diff --check. Все проверки успешны. Семантика SQL не менялась; PostgreSQL локально не запускался. Правка ограничена блокером этого ревью; неблокирующий «Хвост» оставлен вне доработки по процедуре. Новый HEAD 983c92b отправлен отдельным обычным коммитом через CAS-push с ожидаемым исходным SHA; ветка fork, HEAD PR, родитель и PP-Fix-Transition подтверждены через REST. Локальные проверки не заменяют обязательный CI нового точного HEAD. Далее нужны независимое REVIEW и все восемь обязательных CI-проверок. Generated-with: Codex |
|
Ревью. (круг 3)
|
|
PromptPilot service marker: REVIEW result publication claimed. |
|
PromptPilot service marker: REVIEW result committed. |
|
Автоматическое обновление PR #1923 актуальным main; разрешение ship сохранено для последующего интеграционного REVIEW. |
|
PR #1923 обновлён актуальным main. Переход HEAD и сохранение ship подтверждены; требуется интеграционное REVIEW нового HEAD. |
|
Ревью. (круг 4)
|
|
PromptPilot service marker: REVIEW result publication claimed. |
|
PromptPilot service marker: REVIEW result committed. |
|
PromptPilot service marker: MERGE transaction reserved. |
|
PromptPilot service marker: MERGE cleanup completed. |
Режим работы
Что было
Поле под маской (ПДн,
pii: true/field_access mask_*) нельзя было заполнить в записанном объекте, а набранное в форме терялось. Реальный сценарий (колл-центр): заявку из звонка код создаёт и записывает, потом открывает оператору для оформления; телефоны у оператора под маской.protectMaskedFieldsOnWriteвосстанавливал из базы каждое поле под маской — пустое тоже. Введённый оператором телефон молча отбрасывался (проверено на живом экземпляре:Заказчикзаписался,ТелефонМобильный— нет).form-eventмаскировал все защищённые поля, включая номер, который пользователь только что набрал. Следующее событие («Принять заявку» → ответ на вопрос) или запись присылали на сервер уже••••••; после правки п. 1 в пустое поле легли бы сами звёздочки (так и случилось на стенде с первой версией этого PR).Что стало
access.MaskedEmptyFillable(decision, stored, submitted): пустое подmask_*(роль видит его пустым) заполнить можно; заполненное по-прежнему не меняется;hideне заполняется никогда; значение со знаком маски (access.LooksMasked) не заполняет ничего — это маска, которую показала форма. Общая точка для формы/обработчиков/DSL (ui) и REST (api).submittedFieldsToEcho: ответ события отдаёт без маски ровно то, что клиент прислал в этом запросе (он это значение и так знает). Значение из базы (клиент прислал маску) и поставленное обработчиком уходят маской, как раньше.mask_all) в управляемой форме помеченоdata-ob-protectedи показывается точками, как пароль (-webkit-text-security: disc): и маска из базы, и набираемый номер.mask_tail/mask_cityпоказывают часть значения намеренно — без точек.managed.js applyValues: ответ события не трогает поле, которое пользователь изменил, пока шёл запрос, если сервер вернул ровно отправленное — раньше набранные за это время символы стирались (на живой форме пропала последняя цифра телефона).Пользовательский результат и границы
Роль с маской на ПДн может ввести телефон в записанный документ, видит его точками, и он не подменяется звёздочками ни в форме, ни в базе. Изменить или стереть уже сохранённое она не может. Не входит: автоформы (не managed) — пометка точками только в управляемых; код под маской по-прежнему видит ПДн маской (выгрузку ПДн во внешние системы прикладная конфигурация делает фоновым заданием).
Проверки
Через публичные маршруты (
s.Mount):TestUI_SaveCard_MaskedEmptyFieldCanBeFilled—POST /ui/catalog/...: пустое заполняется (mask_tail, pii/mask_all); заполненное и маска не меняют; маска в пустое не пишется; hide не заполняется.TestUI_FormEvent_EchoesTypedMaskedValue—POST .../form-event: набранное возвращается как есть; поставленное обработчиком и значение из базы — маской. Мутационно: без правки «получено ••••••».TestUI_ManagedForm_ProtectedInputRenderedAsPassword—GETформы: поле под маской помечено, настоящего значения в разметке нет; без маски — значение и без пометки.TestManagedApplyValuesKeepsTypingInNode→static/managed_apply_typing_test.js(настоящий applyValues): набранное не стирается эхом; изменённое сервером применяется. Мутационно: без правки падает.TestAPI_FieldMask_EmptyMaskedFieldCanBeFilled(REST PUT),TestMaskedEmptyFillable*(таблица стратегий, маска).go test ./internal/ui ./internal/api ./internal/access,go vet— зелёные.internal/access/field_access.go, отчёты v2) и feat(dsl): СериализаторXDTO — сериализация объекта в XML формата 1С и обратный разбор #1386 (internal/ui/field_access.go, XDTO) — другие задачи и функции.🤖 Generated with Claude Code