Реализация отладки CSP и доверенных типов в Chrome DevTools

Катерина Прокопенко
Kateryna Prokopenko
Альфонсо Кастаньо
Alfonso Castaño

В этом сообщении блога рассказывается о реализации поддержки DevTools для отладки проблем Content Security Policy (CSP) с помощью недавно появившейся вкладки «Проблемы» .

Работа по внедрению проводилась в ходе двух стажировок: 1. В ходе первой мы построили общую структуру отчетности и разработали сообщения о проблемах для 3 проблем с нарушением CSP. 2. Во время второго этапа мы добавили проблемы с доверенными типами наряду с некоторыми специализированными функциями DevTools для отладки доверенных типов.

Что такое политика безопасности контента?

Политика безопасности контента (CSP) позволяет ограничить определенные действия на веб-сайте для повышения безопасности. Например, CSP можно использовать для запрета встроенных сценариев или запрета eval , оба из которых уменьшают поверхность атаки для атак с использованием межсайтовых сценариев (XSS) . Подробное введение в CSP читайте здесь .

Особенно новым CSP является политика Trusted Types (TT) , которая обеспечивает динамический анализ, который может систематически предотвращать большой класс атак с внедрением на веб-сайты. Для достижения этой цели TT поддерживает веб-сайт, контролируя его код JavaScript, чтобы разрешать назначать приемникам DOM только определенные типы вещей, такие как InnerHTML.

Веб-сайт может активировать политику безопасности контента, включив определенный заголовок HTTP. Например, заголовок content-security-policy: require-trusted-types-for 'script'; trusted-types default активируют политику TT для страницы.

Каждая политика может работать в одном из следующих режимов:

  • принудительный режим – где каждое нарушение политики является ошибкой,
  • режим только для отчетов : сообщение об ошибке выводится в виде предупреждения, но не вызывает сбоя на веб-странице.

Реализация проблем политики безопасности контента на вкладке «Проблемы »

Целью этой работы было улучшение процесса отладки проблем CSP. При рассмотрении новых проблем команда DevTools примерно следует следующему процессу:

  1. Определение пользовательских историй . Определите набор пользовательских историй в интерфейсе DevTools, описывающих, как веб-разработчику необходимо исследовать проблему.
  2. Фронтальная реализация . На основе пользовательских историй определите, какие фрагменты информации необходимы для исследования проблемы во внешнем интерфейсе (например, связанный запрос, имя файла cookie, строка в скрипте или html-файле и т. д.).
  3. Обнаружение проблемы . Определите места в браузере, где проблема может быть обнаружена в Chrome, и инструментируйте место для сообщения о проблеме, включая соответствующую информацию из шага (2).
  4. Сохраните и отобразите проблемы . Сохраните проблемы в соответствующем месте и сделайте их доступными для DevTools после открытия.
  5. Разработка текста задачи . Придумайте поясняющий текст, который поможет веб-разработчику понять, а главное решить проблему.

Шаг 1. Определение пользовательских историй для проблем CSP

Прежде чем начать работу по реализации, мы создали проектный документ с пользовательскими историями, чтобы лучше понять, что нам нужно делать. Например, мы записали следующую пользовательскую историю:


Как разработчик, который только что осознал, что какая-то часть моего веб-сайта заблокирована, я хочу: - - ...выяснить, является ли CSP причиной блокировки iframe/изображений на моем веб-сайте - ...узнать, какая директива CSP вызывает блокировка определенного ресурса - ... знаю, как изменить CSP моего веб-сайта, чтобы разрешить отображение заблокированных в данный момент ресурсов / выполнение заблокированных в данный момент js.


Чтобы изучить эту пользовательскую историю, мы создали несколько простых примеров веб-страниц, демонстрирующих интересующие нас нарушения CSP, и изучили примеры страниц, чтобы самостоятельно ознакомиться с процессом. Вот некоторые примеры веб-страниц (откройте демо-версию с открытой вкладкой «Проблемы» ):

Используя этот процесс, мы узнали, что расположение источника является наиболее важной информацией для отладки проблем CSP. Мы также сочли полезным быстро найти связанный iframe и запрос в случае блокировки ресурса, а также прямую ссылку на элемент HTML на панели «Элементы» в DevTools.

Шаг 2: внешняя реализация

Мы превратили это понимание в первый черновик информации, которую хотели сделать доступной для DevTools через протокол Chrome DevTools (CDP) :

Ниже приведен отрывок из Third_party/blink/public/devtools_protocol/browser_protocol.pdl.

 type ContentSecurityPolicyIssueDetails extends object
   properties
     # The url not included in allowed sources.
     optional string blockedURL
     # Specific directive that is violated, causing the CSP issue.
     string violatedDirective
     boolean isReportOnly
     ContentSecurityPolicyViolationType contentSecurityPolicyViolationType
     optional AffectedFrame frameAncestor
     optional SourceCodeLocation sourceCodeLocation
     optional DOM.BackendNodeId violatingNodeId

Приведенное выше определение по существу кодирует структуру данных JSON. Он написан на простом языке PDL (язык данных протокола). PDL используется для двух целей. Во-первых, мы используем PDL для генерации определений TypeScript, на которые опирается интерфейс DevTools. Например, приведенное выше определение PDL генерирует следующий интерфейс TypeScript:

export interface ContentSecurityPolicyIssueDetails {
  /**
  * The url not included in allowed sources.
  */
  blockedURL?: string;
  /**
  * Specific directive that is violated, causing the CSP issue.
  */
  violatedDirective: string;
  isReportOnly: boolean;
  contentSecurityPolicyViolationType: ContentSecurityPolicyViolationType;
  frameAncestor?: AffectedFrame;
  sourceCodeLocation?: SourceCodeLocation;
  violatingNodeId?: DOM.BackendNodeId;
}

Во-вторых, что, вероятно, более важно, мы генерируем библиотеку C++ из определения, которая обрабатывает создание и отправку этих структур данных из серверной части C++ Chromium во внешний интерфейс DevTools. Используя эту библиотеку, объект ContentSecurityPolicyIssueDetails можно создать с помощью следующего фрагмента кода C++:

protocol::Audits::ContentSecurityPolicyIssueDetails::create()
  .setViolatedDirective(d->violated_directive)
  .setIsReportOnly(d->is_report_only)
  .setContentSecurityPolicyViolationType(BuildViolationType(
      d->content_security_policy_violation_type)))
  .build();

После того, как мы определились с тем, какую информацию мы хотим сделать доступной, нам нужно было выяснить, где получить эту информацию из Chromium.

Шаг 3: обнаружение проблемы

Чтобы сделать информацию доступной для протокола Chrome DevTools (CDP) в формате, описанном в последнем разделе, нам нужно было найти место, где информация была действительно доступна на серверной стороне. К счастью, в коде CSP уже было узкое место, используемое для режима только отчетов, куда мы могли подключиться: ContentSecurityPolicy::ReportViolation сообщает о проблемах (необязательной) конечной точке отчетов, которую можно настроить в HTTP-заголовке CSP . Большая часть информации, которую мы хотели сообщить, уже была доступна, поэтому для работы нашего инструментария не потребовалось никаких больших изменений в серверной части.

Шаг 4: сохраните и отобразите проблемы

Небольшая сложность заключается в том, что мы также хотели сообщать о проблемах, возникших до открытия DevTools, аналогично тому, как обрабатываются сообщения консоли. Это означает, что мы не сообщаем о проблемах сразу во внешний интерфейс, а используем хранилище, заполненное проблемами, независимо от того, открыты DevTools или нет. После открытия DevTools (или, если уж на то пошло, любого другого клиента CDP), все ранее записанные проблемы можно воспроизвести из хранилища.

На этом внутренняя работа завершилась, и теперь нам нужно было сосредоточиться на том, как выявить проблему во внешней части.

Шаг 5: разработка текста задачи

Разработка текста проблем — это процесс, в котором участвуют несколько команд, помимо нашей. Например, мы часто полагаемся на мнение команды, реализующей функцию (в данном случае это команда CSP), и, конечно же, команды DevRel, которая разрабатывает как веб-разработчики должны решать проблемы определенного типа. Текст выпуска обычно проходит некоторую доработку, пока не будет завершен.

Обычно команда DevTools начинает с чернового наброска того, что они себе представляют:


## Header
Content Security Policy: include all sources of your resources in content security policy header to improve the functioning of your site

## General information
Even though some sources are included in the content security policy header, some resources accessed by your site like images, stylesheets or scripts originate from sources not included in content security policy directives.

Usage of content from not included sources is restricted to strengthen the security of your entire site.

## Specific information

### VIOLATED DIRECTIVES
`img-src 'self'`

### BLOCKED URLs
https://imgur.com/JuXCo1p.jpg

## Specific information
https://web.dev/strict-csp/

После итерации мы пришли к следующему:

ALT_TEXT_ЗДЕСЬ

Как видите, участие функциональной команды и DevRel делает описание намного более ясным и точным!

Проблемы CSP на вашей странице также можно обнаружить на вкладке, специально посвященной нарушениям CSP .

Отладка проблем с доверенными типами

Работа с TT в больших масштабах может оказаться сложной задачей без подходящих инструментов разработчика.

Улучшенная консольная печать.

Когда мы работаем с доверенными объектами, нам хотелось бы отображать как минимум тот же объем информации, что и для недоверенного аналога. К сожалению, в настоящее время при отображении доверенного объекта информация об обернутом объекте не отображается.

Это связано с тем, что значение, отображаемое в консоли, по умолчанию берется из вызова .valueOf() для объекта. Однако в случае доверенного типа возвращаемое значение не очень полезно. Вместо этого мы хотели бы иметь что-то похожее на то, что вы получаете при вызове .toString() . Чтобы добиться этого, нам нужно изменить V8 и Blink, чтобы ввести специальную обработку для объектов доверенного типа.

Хотя по историческим причинам эта специальная обработка была реализована в V8, такой подход имеет важные недостатки. Существует множество объектов, которые требуют индивидуального отображения, но тип которых одинаков на уровне JS. Поскольку V8 — это чистый JS, он не может различать понятия, соответствующие веб-API, такие как доверенный тип. По этой причине V8 приходится обращаться к специалисту по внедрению (Blink) за помощью, чтобы отличить их.

Следовательно, перемещение этой части кода в Blink или любое другое средство внедрения кажется логичным выбором. Помимо выявленной проблемы, есть много других преимуществ:

  • У каждого средства внедрения может быть своя собственная генерация описания.
  • Гораздо проще создать описание через Blink API.
  • У Блинка есть доступ к исходному определению объекта. Таким образом, если мы используем .toString() для генерации описания, нет риска, что .toString() может быть переопределен.

Разрыв при нарушении (в режиме только отчета)

В настоящее время единственным способом отладки нарушений TT является установка точек останова на исключениях JS. Поскольку принудительные нарушения TT ​​вызывают исключение, эта функция может быть чем-то полезна. Однако в реальных сценариях вам необходим более детальный контроль над нарушениями TT. В частности, мы хотели бы делать перерывы только в отношении нарушений TT (а не других исключений), а также входить в режим только отчетов и различать различные типы нарушений TT.

DevTools уже поддерживает широкий спектр точек останова, поэтому архитектура достаточно расширяема. Добавление нового типа точки останова требует изменений в серверной части (Blink), CDP и внешнем интерфейсе. Нам следует ввести новую команду CDP, назовем ее setBreakOnTTViolation . Эта команда будет использоваться интерфейсом, чтобы сообщить серверу, какие нарушения TT ​​следует устранять. Бэкэнд, в частности InspectorDOMDebuggerAgent , предоставит «зонд» onTTViolation() , который будет вызываться каждый раз, когда происходит нарушение TT. Затем InspectorDOMDebuggerAgent проверит, должно ли это нарушение вызывать точку останова, и если это так, он отправит сообщение во внешний интерфейс, чтобы приостановить выполнение.

Что сделано и что дальше?

С момента появления описанных здесь проблем вкладка «Проблемы» претерпела некоторые изменения:

В дальнейшем мы планируем использовать вкладку «Проблемы» для выявления большего количества проблем, что в конечном итоге позволит разгрузить консоль от потока нечитаемых сообщений об ошибках.

Загрузите предварительный просмотр каналов

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра предоставляют вам доступ к новейшим функциям DevTools, позволяют тестировать передовые API-интерфейсы веб-платформы и помогают находить проблемы на вашем сайте раньше, чем это сделают ваши пользователи!

Свяжитесь с командой Chrome DevTools

Используйте следующие параметры, чтобы обсудить новые функции, обновления или что-либо еще, связанное с DevTools.

,

Катерина Прокопенко
Kateryna Prokopenko
Альфонсо Кастаньо
Alfonso Castaño

В этом сообщении блога рассказывается о реализации поддержки DevTools для отладки проблем Content Security Policy (CSP) с помощью недавно появившейся вкладки «Проблемы» .

Работа по внедрению проводилась в ходе двух стажировок: 1. В ходе первой мы построили общую структуру отчетности и разработали сообщения о проблемах для 3 проблем с нарушением CSP. 2. Во время второго этапа мы добавили проблемы с доверенными типами наряду с некоторыми специализированными функциями DevTools для отладки доверенных типов.

Что такое политика безопасности контента?

Политика безопасности контента (CSP) позволяет ограничить определенные действия на веб-сайте для повышения безопасности. Например, CSP можно использовать для запрета встроенных сценариев или запрета eval , оба из которых уменьшают поверхность атаки для атак с использованием межсайтовых сценариев (XSS) . Подробное введение в CSP читайте здесь .

Особенно новым CSP является политика Trusted Types (TT) , которая обеспечивает динамический анализ, который может систематически предотвращать большой класс атак путем внедрения на веб-сайты. Для достижения этой цели TT поддерживает веб-сайт, контролируя его код JavaScript, чтобы разрешать назначать приемникам DOM только определенные типы вещей, такие как InnerHTML.

Веб-сайт может активировать политику безопасности контента, включив определенный заголовок HTTP. Например, заголовок content-security-policy: require-trusted-types-for 'script'; trusted-types default активируют политику TT для страницы.

Каждая политика может работать в одном из следующих режимов:

  • принудительный режим – где каждое нарушение политики является ошибкой,
  • режим только для отчетов : сообщение об ошибке выводится в виде предупреждения, но не вызывает сбоя на веб-странице.

Реализация проблем политики безопасности контента на вкладке «Проблемы »

Целью этой работы было улучшение процесса отладки проблем CSP. При рассмотрении новых проблем команда DevTools примерно следует следующему процессу:

  1. Определение пользовательских историй . Определите набор пользовательских историй в интерфейсе DevTools, описывающих, как веб-разработчику необходимо исследовать проблему.
  2. Фронтальная реализация . На основе пользовательских историй определите, какие фрагменты информации необходимы для исследования проблемы во внешнем интерфейсе (например, связанный запрос, имя файла cookie, строка в скрипте или html-файле и т. д.).
  3. Обнаружение проблемы . Определите места в браузере, где проблема может быть обнаружена в Chrome, и инструментируйте место для сообщения о проблеме, включая соответствующую информацию из шага (2).
  4. Сохраните и отобразите проблемы . Сохраните проблемы в соответствующем месте и сделайте их доступными для DevTools после открытия.
  5. Разработка текста задачи . Придумайте поясняющий текст, который поможет веб-разработчику понять, а главное решить проблему.

Шаг 1. Определение пользовательских историй для проблем CSP

Прежде чем начать работу по реализации, мы создали проектный документ с пользовательскими историями, чтобы лучше понять, что нам нужно делать. Например, мы записали следующую пользовательскую историю:


Как разработчик, который только что осознал, что какая-то часть моего веб-сайта заблокирована, я хочу: - - ...выяснить, является ли CSP причиной блокировки iframe/изображений на моем веб-сайте - ...узнать, какая директива CSP вызывает блокировка определенного ресурса - ... знаю, как изменить CSP моего веб-сайта, чтобы разрешить отображение заблокированных в данный момент ресурсов / выполнение заблокированных в данный момент js.


Чтобы изучить эту пользовательскую историю, мы создали несколько простых примеров веб-страниц, демонстрирующих интересующие нас нарушения CSP, и изучили примеры страниц, чтобы самостоятельно ознакомиться с процессом. Вот некоторые примеры веб-страниц (откройте демо-версию с открытой вкладкой «Проблемы» ):

Используя этот процесс, мы узнали, что расположение источника является наиболее важной информацией для отладки проблем CSP. Мы также сочли полезным быстро найти связанный iframe и запрос в случае блокировки ресурса, а также что прямая ссылка на элемент HTML на панели «Элементы» DevTools также может быть полезной.

Шаг 2: внешняя реализация

Мы превратили это понимание в первый черновик информации, которую хотели сделать доступной для DevTools через протокол Chrome DevTools (CDP) :

Ниже приведен отрывок из Third_party/blink/public/devtools_protocol/browser_protocol.pdl.

 type ContentSecurityPolicyIssueDetails extends object
   properties
     # The url not included in allowed sources.
     optional string blockedURL
     # Specific directive that is violated, causing the CSP issue.
     string violatedDirective
     boolean isReportOnly
     ContentSecurityPolicyViolationType contentSecurityPolicyViolationType
     optional AffectedFrame frameAncestor
     optional SourceCodeLocation sourceCodeLocation
     optional DOM.BackendNodeId violatingNodeId

Приведенное выше определение по существу кодирует структуру данных JSON. Он написан на простом языке PDL (язык данных протокола). PDL используется для двух целей. Во-первых, мы используем PDL для генерации определений TypeScript, на которые опирается интерфейс DevTools. Например, приведенное выше определение PDL генерирует следующий интерфейс TypeScript:

export interface ContentSecurityPolicyIssueDetails {
  /**
  * The url not included in allowed sources.
  */
  blockedURL?: string;
  /**
  * Specific directive that is violated, causing the CSP issue.
  */
  violatedDirective: string;
  isReportOnly: boolean;
  contentSecurityPolicyViolationType: ContentSecurityPolicyViolationType;
  frameAncestor?: AffectedFrame;
  sourceCodeLocation?: SourceCodeLocation;
  violatingNodeId?: DOM.BackendNodeId;
}

Во-вторых, что, вероятно, более важно, мы генерируем библиотеку C++ из определения, которая обрабатывает создание и отправку этих структур данных из серверной части C++ Chromium во внешний интерфейс DevTools. Используя эту библиотеку, объект ContentSecurityPolicyIssueDetails можно создать с помощью следующего фрагмента кода C++:

protocol::Audits::ContentSecurityPolicyIssueDetails::create()
  .setViolatedDirective(d->violated_directive)
  .setIsReportOnly(d->is_report_only)
  .setContentSecurityPolicyViolationType(BuildViolationType(
      d->content_security_policy_violation_type)))
  .build();

После того, как мы определились с тем, какую информацию мы хотим сделать доступной, нам нужно было выяснить, где получить эту информацию из Chromium.

Шаг 3: обнаружение проблемы

Чтобы сделать информацию доступной для протокола Chrome DevTools (CDP) в формате, описанном в последнем разделе, нам нужно было найти место, где информация была действительно доступна на серверной стороне. К счастью, в коде CSP уже было узкое место, используемое для режима только отчетов, куда мы могли подключиться: ContentSecurityPolicy::ReportViolation сообщает о проблемах (необязательной) конечной точке отчетов, которую можно настроить в HTTP-заголовке CSP . Большая часть информации, которую мы хотели сообщить, уже была доступна, поэтому для работы нашего инструментария не потребовалось никаких больших изменений в серверной части.

Шаг 4: сохраните и отобразите проблемы

Небольшая сложность заключается в том, что мы также хотели сообщать о проблемах, возникших до открытия DevTools, аналогично тому, как обрабатываются сообщения консоли. Это означает, что мы не сообщаем о проблемах сразу во внешний интерфейс, а используем хранилище, заполненное проблемами, независимо от того, открыты DevTools или нет. После открытия DevTools (или, если уж на то пошло, любого другого клиента CDP), все ранее записанные проблемы можно воспроизвести из хранилища.

На этом внутренняя работа завершилась, и теперь нам нужно было сосредоточиться на том, как выявить проблему во внешней части.

Шаг 5: разработка текста задачи

Разработка текста проблем — это процесс, в котором участвуют несколько команд, помимо нашей. Например, мы часто полагаемся на мнение команды, реализующей функцию (в данном случае это команда CSP), и, конечно же, команды DevRel, которая разрабатывает как веб-разработчики должны решать проблемы определенного типа. Текст выпуска обычно проходит некоторую доработку, пока не будет закончен.

Обычно команда DevTools начинает с чернового наброска того, что они себе представляют:


## Header
Content Security Policy: include all sources of your resources in content security policy header to improve the functioning of your site

## General information
Even though some sources are included in the content security policy header, some resources accessed by your site like images, stylesheets or scripts originate from sources not included in content security policy directives.

Usage of content from not included sources is restricted to strengthen the security of your entire site.

## Specific information

### VIOLATED DIRECTIVES
`img-src 'self'`

### BLOCKED URLs
https://imgur.com/JuXCo1p.jpg

## Specific information
https://web.dev/strict-csp/

После итерации мы пришли к следующему:

ALT_TEXT_ЗДЕСЬ

Как видите, участие функциональной команды и DevRel делает описание намного более ясным и точным!

Проблемы CSP на вашей странице также можно обнаружить на вкладке, специально посвященной нарушениям CSP .

Отладка проблем с доверенными типами

Работа с TT в больших масштабах может оказаться сложной задачей без подходящих инструментов разработчика.

Улучшенная консольная печать.

Когда мы работаем с доверенными объектами, нам хотелось бы отображать как минимум тот же объем информации, что и для недоверенного аналога. К сожалению, в настоящее время при отображении доверенного объекта информация об обернутом объекте не отображается.

Это связано с тем, что значение, отображаемое в консоли, по умолчанию берется из вызова .valueOf() для объекта. Однако в случае доверенного типа возвращаемое значение не очень полезно. Вместо этого мы хотели бы иметь что-то похожее на то, что вы получаете при вызове .toString() . Чтобы добиться этого, нам нужно изменить V8 и Blink, чтобы ввести специальную обработку для объектов доверенного типа.

Хотя по историческим причинам эта специальная обработка была реализована в V8, такой подход имеет важные недостатки. Существует множество объектов, которые требуют индивидуального отображения, но тип которых одинаков на уровне JS. Поскольку V8 — это чистый JS, он не может различать понятия, соответствующие веб-API, такие как доверенный тип. По этой причине V8 приходится обращаться к специалисту по внедрению (Blink) за помощью, чтобы отличить их.

Следовательно, перемещение этой части кода в Blink или любое другое средство внедрения кажется логичным выбором. Помимо выявленной проблемы, есть много других преимуществ:

  • У каждого средства внедрения может быть своя собственная генерация описания.
  • Гораздо проще создать описание через Blink API.
  • У Блинка есть доступ к исходному определению объекта. Таким образом, если мы используем .toString() для генерации описания, нет риска, что .toString() может быть переопределен.

Разрыв при нарушении (в режиме только отчета)

В настоящее время единственным способом отладки нарушений TT является установка точек останова на исключениях JS. Поскольку принудительные нарушения TT ​​вызывают исключение, эта функция может быть чем-то полезна. Однако в реальных сценариях вам необходим более детальный контроль над нарушениями TT. В частности, мы хотели бы делать перерывы только в отношении нарушений TT (а не других исключений), а также входить в режим только отчетов и различать различные типы нарушений TT.

DevTools уже поддерживает широкий спектр точек останова, поэтому архитектура достаточно расширяема. Добавление нового типа точки останова требует изменений в серверной части (Blink), CDP и внешнем интерфейсе. Нам следует ввести новую команду CDP, назовем ее setBreakOnTTViolation . Эта команда будет использоваться интерфейсом, чтобы сообщить серверу, какие нарушения TT ​​следует устранять. Бэкэнд, в частности InspectorDOMDebuggerAgent , предоставит «зонд» onTTViolation() , который будет вызываться каждый раз, когда происходит нарушение TT. Затем InspectorDOMDebuggerAgent проверит, должно ли это нарушение вызывать точку останова, и если это так, он отправит сообщение во внешний интерфейс, чтобы приостановить выполнение.

Что сделано и что дальше?

С момента появления описанных здесь проблем вкладка «Проблемы» претерпела некоторые изменения:

В дальнейшем мы планируем использовать вкладку «Проблемы» для выявления большего количества проблем, что в конечном итоге позволит разгрузить консоль от потока нечитаемых сообщений об ошибках.

Загрузите предварительный просмотр каналов

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра предоставляют вам доступ к новейшим функциям DevTools, позволяют тестировать передовые API-интерфейсы веб-платформы и помогают находить проблемы на вашем сайте раньше, чем это сделают ваши пользователи!

Свяжитесь с командой Chrome DevTools

Используйте следующие параметры, чтобы обсудить новые функции, обновления или что-либо еще, связанное с DevTools.