[{"data":1,"prerenderedAt":959},["ShallowReactive",2],{"navigation_docs":3,"-modules-insync-security":238,"-modules-insync-security-surround":956},[4,45,60],{"title":5,"description":6,"icon":7,"path":8,"stem":9,"children":10},"Методология",null,"ruler","\u002Fmethodology","1.methodology",[11,15,20,25,30,35,40],{"title":12,"path":8,"stem":13,"description":14},"bxshef: методология и проверка навыков","1.methodology\u002Findex","Как писать навыки ИИ-агентов для Битрикса и как проверять, что им можно верить",{"title":16,"path":17,"stem":18,"description":19},"Стандарт навыка","\u002Fmethodology\u002Fstandard","1.methodology\u002F1.standard","11 правил, каждое из провала на стенде",{"title":21,"path":22,"stem":23,"description":24},"Методология проверки","\u002Fmethodology\u002Fmethod","1.methodology\u002F2.method","lint → eval → стенд; что измерено",{"title":26,"path":27,"stem":28,"description":29},"bxshef — CLI","\u002Fmethodology\u002Fbxshef","1.methodology\u002F3.bxshef","lint, eval, feedback",{"title":31,"path":32,"stem":33,"description":34},"GitHub Action","\u002Fmethodology\u002Faction","1.methodology\u002F4.action","тот же lint + eval в любом репозитории навыков",{"title":36,"path":37,"stem":38,"description":39},"Заготовка репозитория","\u002Fmethodology\u002Ftemplate","1.methodology\u002F5.template","с чего начать автору модуля",{"title":41,"path":42,"stem":43,"description":44},"Приёмник отзывов","\u002Fmethodology\u002Ffeedback","1.methodology\u002F6.feedback","куда уходят отзывы ИИ-агентов",{"title":46,"description":6,"icon":47,"path":48,"stem":49,"children":50},"Навыки","sparkles","\u002Fskills","2.skills",[51,55],{"title":52,"path":48,"stem":53,"description":54},"Навыки shef.*","2.skills\u002Findex","npx skills add bx-shef\u002Fskills",{"title":56,"path":57,"stem":58,"description":59},"shef-feedback","\u002Fskills\u002Fshef-feedback","2.skills\u002F1.shef-feedback","Отправить отзыв о навыке shef-* или навыке команды. Брать ВСЕГДА, когда просят оставить или отправить отзыв о навыке — даже если сам навык в диалоге не виден, —",{"title":61,"description":6,"icon":62,"path":63,"stem":64,"children":65},"Модули shef.*","package","\u002Fmodules","3.modules",[66,69,131,179],{"title":61,"path":63,"stem":67,"description":68},"3.modules\u002Findex","Что за чем ставить",{"title":70,"description":6,"path":71,"stem":72,"children":73},"shef.options","\u002Fmodules\u002Foptions","3.modules\u002F1.options\u002Findex",[74,76,81,86,91,96,101,106,110,114,119,123,127],{"title":70,"path":71,"stem":72,"description":75},"фундамент: настройки, трейты, компоненты",{"title":77,"path":78,"stem":79,"description":80},"[`\\Shef\\Options\\Installator`] Installer","\u002Fmodules\u002Foptions\u002Finstaller","3.modules\u002F1.options\u002F1.installer","Для облегчения установок.",{"title":82,"path":83,"stem":84,"description":85},"Сборка, CI и релиз","\u002Fmodules\u002Foptions\u002Fbuild-and-install","3.modules\u002F1.options\u002F10.build-and-install","Раскладка репозитория — в module-structure.md, процесс —\nв CONTRIBUTING.md, установка глазами пользователя —\nв README.md.",{"title":87,"path":88,"stem":89,"description":90},"Раскладка репозитория","\u002Fmodules\u002Foptions\u002Fmodule-structure","3.modules\u002F1.options\u002F11.module-structure","Файл про устройство репозитория. Опорные точки модуля — в CLAUDE.md,\nпроцесс — в CONTRIBUTING.md, сборка — в\nbuild-and-install.md.",{"title":92,"path":93,"stem":94,"description":95},"Проверка на портале","\u002Fmodules\u002Foptions\u002Fportal-check","3.modules\u002F1.options\u002F12.portal-check","Всё, что ниже рантайма Битрикса, тестами не закрыть: установка, права, кеш,\nраскладка файлов, поведение при обновлении. Проверять это приходится руками —\nи лучше по списку, потому что забытый шаг находит не разработчик, а клиент.",{"title":97,"path":98,"stem":99,"description":100},"[`\\Shef\\Options\\Main\\Options`] Опции настроек модуля","\u002Fmodules\u002Foptions\u002Foptions","3.modules\u002F1.options\u002F2.options","Поддерживает следующие типы:",{"title":102,"path":103,"stem":104,"description":105},"Работа с пользователями","\u002Fmodules\u002Foptions\u002Fsecurity","3.modules\u002F1.options\u002F3.security","",{"title":107,"path":108,"stem":109,"description":105},"[`\\Shef\\Options\\Main\\Utils`] Утилиты","\u002Fmodules\u002Foptions\u002Futils","3.modules\u002F1.options\u002F4.utils",{"title":111,"path":112,"stem":113,"description":105},"[`\\Shef\\Options\\Components`] Работа с компонентами","\u002Fmodules\u002Foptions\u002Fcomponents","3.modules\u002F1.options\u002F5.components",{"title":115,"path":116,"stem":117,"description":118},"[`\\Shef\\Options\\Options`] Паттерны","\u002Fmodules\u002Foptions\u002Fpattern","3.modules\u002F1.options\u002F6.pattern","Запускаемые примеры: singleton.php,\nconfig.php, smartstd.php —\nкак их гонять, написано в examples\u002FREADME.md.",{"title":120,"path":121,"stem":122,"description":105},"[`\\Shef\\Options\\Tests`] Тестирование","\u002Fmodules\u002Foptions\u002Ftests","3.modules\u002F1.options\u002F7.tests",{"title":124,"path":125,"stem":126,"description":105},"[`\\Shef\\Options\\TraitList`] Набор трейтов","\u002Fmodules\u002Foptions\u002Ftraitlist","3.modules\u002F1.options\u002F8.traitlist",{"title":128,"path":129,"stem":130,"description":105},"Правила для ИИ-агентов в этом репозитории","\u002Fmodules\u002Foptions\u002Fagent-rules","3.modules\u002F1.options\u002F9.agent-rules",{"title":132,"description":6,"path":133,"stem":134,"children":135},"shef.problems","\u002Fmodules\u002Fproblems","3.modules\u002F2.problems\u002Findex",[136,138,143,147,152,157,161,166,169,172,175],{"title":132,"path":133,"stem":134,"description":137},"логи, журнал событий, учёт проблем",{"title":139,"path":140,"stem":141,"description":142},"[`\\Shef\\Problems\\Integration`] События и меню","\u002Fmodules\u002Fproblems\u002Fevents","3.modules\u002F2.problems\u002F1.events","Что модуль регистрирует в ядре при установке. Список — в .settings.php, ключ\ninstallEvents; что каждый обработчик существует, сверяет\ntests\u002Fsettings_test.php.",{"title":144,"path":145,"stem":146,"description":105},"Безопасность логов","\u002Fmodules\u002Fproblems\u002Fsecurity","3.modules\u002F2.problems\u002F10.security",{"title":148,"path":149,"stem":150,"description":151},"Быстрая отладка: `_pr`, `_log`, `_log1`","\u002Fmodules\u002Fproblems\u002Fdeffunctions","3.modules\u002F2.problems\u002F2.deffunctions","Функции объявляет def-functions.php, подключается он из include.php.",{"title":153,"path":154,"stem":155,"description":156},"Уровни логирования","\u002Fmodules\u002Fproblems\u002Floglevel","3.modules\u002F2.problems\u002F3.loglevel","Уровни — стандарт PSR-3, в Monolog это \\Monolog\\Level. Ниже — когда какой брать.",{"title":158,"path":159,"stem":160,"description":105},"[`\\Shef\\Problems\\Integration\\Monolog`] Monolog","\u002Fmodules\u002Fproblems\u002Fmonolog","3.modules\u002F2.problems\u002F4.monolog",{"title":162,"path":163,"stem":164,"description":165},"Ротация логов","\u002Fmodules\u002Fproblems\u002Flogrotate","3.modules\u002F2.problems\u002F5.logrotate","Логи модуля лежат в каталоге логов — по умолчанию на уровень выше корня сайта,\nдля BitrixVM \u002Fhome\u002Fbitrix\u002Fsh_log\u002F*.log, — и сами не чистятся. Ротацию делает\nlogrotate — системная утилита, а не Monolog: Monolog тоже умеет\n(RotatingFileHandler), но тогда за файлами следит каждый PHP-процесс, а не\nодна служба.",{"title":128,"path":167,"stem":168,"description":105},"\u002Fmodules\u002Fproblems\u002Fagent-rules","3.modules\u002F2.problems\u002F6.agent-rules",{"title":82,"path":170,"stem":171,"description":85},"\u002Fmodules\u002Fproblems\u002Fbuild-and-install","3.modules\u002F2.problems\u002F7.build-and-install",{"title":87,"path":173,"stem":174,"description":90},"\u002Fmodules\u002Fproblems\u002Fmodule-structure","3.modules\u002F2.problems\u002F8.module-structure",{"title":92,"path":176,"stem":177,"description":178},"\u002Fmodules\u002Fproblems\u002Fportal-check","3.modules\u002F2.problems\u002F9.portal-check","Всё, что ниже рантайма Битрикса, тестами не закрыть: установка, права, меню,\nраскладка файлов, журнал событий, поведение при обновлении. Проверять это\nприходится руками — и лучше по списку, потому что забытый шаг находит не\nразработчик, а клиент.",{"title":180,"description":6,"path":181,"stem":182,"children":183},"shef.insync","\u002Fmodules\u002Finsync","3.modules\u002F3.insync\u002Findex",[184,186,191,194,198,203,208,213,218,222,227,232,235],{"title":180,"path":181,"stem":182,"description":185},"агенты, импорт, API-клиенты, модели",{"title":187,"path":188,"stem":189,"description":190},"[`\\Shef\\InSync\\Agents`] Агенты","\u002Fmodules\u002Finsync\u002Fagents","3.modules\u002F3.insync\u002F1.agents","Агент импорта — наследник \\Shef\\InSync\\Agents\\AAgent. Описание агента для\nb_agent — \\Shef\\InSync\\Agents\\Entity, установку и управление берёт на себя\n\\Shef\\InSync\\Agents\\Manager.",{"title":87,"path":192,"stem":193,"description":90},"\u002Fmodules\u002Finsync\u002Fmodule-structure","3.modules\u002F3.insync\u002F10.module-structure",{"title":92,"path":195,"stem":196,"description":197},"\u002Fmodules\u002Finsync\u002Fportal-check","3.modules\u002F3.insync\u002F11.portal-check","Всё, что ниже рантайма Битрикса, тестами не закрыть: установка, права, левое\nменю, раскладка компонентов, агенты, таблица импорта, поведение при\nобновлении. Проверять это приходится руками — и лучше по списку, потому что\nзабытый шаг находит не разработчик, а клиент.",{"title":199,"path":200,"stem":201,"description":202},"Безопасность","\u002Fmodules\u002Finsync\u002Fsecurity","3.modules\u002F3.insync\u002F12.security","Модуль даёт другим модулям заготовки для синхронизаций, и самые опасные места\nу него общие с ними: кто может запускать агенты и импорт, что ложится в каталоги\nимпорта, что уходит в SQL и в лог. Ниже — что держит модуль и что остаётся\nпроекту.",{"title":204,"path":205,"stem":206,"description":207},"[`\\Shef\\InSync\\Sync`] Импорт","\u002Fmodules\u002Finsync\u002Fimport","3.modules\u002F3.insync\u002F2.import","Импорт идёт в два шага через таблицу импорта \\Shef\\InSync\\Sync\\Model\\SyncTable\n(shef_insync_model):",{"title":209,"path":210,"stem":211,"description":212},"[`\\Shef\\InSync\\Api`] API","\u002Fmodules\u002Finsync\u002Fapi","3.modules\u002F3.insync\u002F3.api","Обращение к внешнему API по HTTP — наследник \\Shef\\InSync\\Api\\AConnector.",{"title":214,"path":215,"stem":216,"description":217},"[`\\Shef\\InSync\\TraitList\\Xml`] Парсинг XML","\u002Fmodules\u002Finsync\u002Fxml","3.modules\u002F3.insync\u002F4.xml","Разбор XML сделан через SbWereWolf\u002Fxml-navigator\n(статья на Хабре): XMLReader идёт по\nдокументу потоком, а каждый элемент с нужным тегом HierarchyComposer\nпревращает в массив — n имя, v значение, a атрибуты, s вложенные\nэлементы.",{"title":219,"path":220,"stem":221,"description":105},"Компоненты","\u002Fmodules\u002Finsync\u002Fcomponents","3.modules\u002F3.insync\u002F5.components",{"title":223,"path":224,"stem":225,"description":226},"Страницы","\u002Fmodules\u002Finsync\u002Fpage","3.modules\u002F3.insync\u002F6.page","Страницы модуля — в левом меню, штатным разделом intranet\n(Bitrix\\Intranet\\CustomSection): раздел «SH Импорт», код shinsync.\nВерхней панели и shef.uiclear больше нет.",{"title":228,"path":229,"stem":230,"description":231},"[`\\Shef\\InSync\\Main\\Options`] Опции настроек модуля","\u002Fmodules\u002Finsync\u002Foptions","3.modules\u002F3.insync\u002F7.options","Опции для страницы настроек модуля импорта (на ShOptionsConfig из\nshef.options): добавьте их во вкладку своего options_conf.php.",{"title":128,"path":233,"stem":234,"description":105},"\u002Fmodules\u002Finsync\u002Fagent-rules","3.modules\u002F3.insync\u002F8.agent-rules",{"title":82,"path":236,"stem":237,"description":85},"\u002Fmodules\u002Finsync\u002Fbuild-and-install","3.modules\u002F3.insync\u002F9.build-and-install",{"id":239,"title":199,"body":240,"description":202,"extension":950,"meta":951,"navigation":952,"path":200,"rawbody":953,"seo":954,"stem":201,"__hash__":955},"docs\u002F3.modules\u002F3.insync\u002F12.security.md",{"type":241,"value":242,"toc":936},"minimark",[243,246,251,258,280,306,317,332,411,417,421,432,471,498,505,510,517,575,586,593,597,608,644,651,665,669,679,727,731,783,787,808,812,830,834,847,851,883,893,897,911,920,932],[244,245,202],"p",{},[247,248,250],"h2",{"id":249},"кто-управляет-импортом","Кто управляет импортом",[244,252,253,257],{},[254,255,256],"code",{},"\\Shef\\InSync\\Main\\Access::canManage()"," — одно правило на весь модуль:",[259,260,261,265,277],"ul",{},[262,263,264],"li",{},"администратор портала — да;",[262,266,267,268,272,273,276],{},"пользователь с правом ",[269,270,271],"strong",{},"«Запись» (W)"," и выше на ",[269,274,275],{},"модуль импорта"," в\n«Настройки → Настройки продукта → Права доступа» — да;",[262,278,279],{},"остальные, включая гостя, — нет.",[244,281,282,283,286,287,290,291,294,295,298,299,302,303,305],{},"Модуль импорта — тот, чей агент или класс импорта: права на ",[254,284,285],{},"shef.demosync","\nоткрывают его импорт, но не агенты ",[254,288,289],{},"main",". Трогать из интерфейса модуля можно\nтолько агенты импорта — наследники ",[254,292,293],{},"\\Shef\\InSync\\Agents\\AAgent",": право «W» на\n",[254,296,297],{},"sale"," или ",[254,300,301],{},"crm"," не открывает их штатные агенты, это остаётся администратору\nв списке агентов ядра. Для страницы статистики и перехода\nк таблице импорта — права на ",[254,304,180],{},".",[244,307,308,309,312,313,316],{},"Проверяется ",[269,310,311],{},"и на показ, и в действии",": адрес ajax-действия виден в коде\nстраницы и вызывается напрямую. До 2.0.0 все действия модуля стояли на\n",[254,314,315],{},"Actions\\Normal"," — это умолчания ядра, вход на портал и csrf, — и любой\nвошедший сотрудник:",[259,318,319,326,329],{},[262,320,321,322,325],{},"включал и выключал ",[269,323,324],{},"любой агент портала"," по ID — и из компонента\nстатистики, и из контроллера страницы настроек;",[262,327,328],{},"загружал файлы в импорт и прогонял агент импорта;",[262,330,331],{},"чистил таблицу импорта.",[333,334,335,348],"table",{},[336,337,338],"thead",{},[339,340,341,345],"tr",{},[342,343,344],"th",{},"где",[342,346,347],{},"что проверяется",[349,350,351,364,377,391,401],"tbody",{},[339,352,353,361],{},[354,355,356,357,360],"td",{},"левое меню (",[254,358,359],{},"Integration\\Intranet\\CustomSectionProvider",")",[354,362,363],{},"страницы модуля видны только тем, кому разрешены",[339,365,366,371],{},[354,367,368],{},[254,369,370],{},"shef.insync:import.stat.local",[354,372,373,374,376],{},"страница и все действия — права на ",[254,375,180],{},"; кнопки агента — права на модуль агента",[339,378,379,384],{},[354,380,381],{},[254,382,383],{},"shef.insync:import.from.file",[354,385,386,387,390],{},"страница и действия — права на модуль импорта, ",[269,388,389],{},"до"," того как подключается модуль и создаётся объект импорта",[339,392,393,398],{},[354,394,395],{},[254,396,397],{},"Main\\Options\\Agent\\Controller",[354,399,400],{},"агент существует, принадлежит модулю из запроса, права на этот модуль",[339,402,403,408],{},[354,404,405],{},[254,406,407],{},"Main\\Options\\Import\\FromFile\\AController",[354,409,410],{},"права на модуль контроллера (из его namespace)",[244,412,413,414,305],{},"Сторожит ",[254,415,416],{},"tests\u002Faccess_test.php",[247,418,420],{"id":419},"каталоги-импорта-вне-корня-сайта","Каталоги импорта — вне корня сайта",[244,422,423,424,427,428,431],{},"Каталог импорта — ",[254,425,426],{},"\\Shef\\InSync\\Main\\Constants::getImportDir()",". По\nумолчанию он на уровень ",[269,429,430],{},"выше"," корня сайта:",[333,433,434,444],{},[336,435,436],{},[339,437,438,441],{},[342,439,440],{},"корень сайта",[342,442,443],{},"каталог импорта",[349,445,446,459],{},[339,447,448,454],{},[354,449,450,453],{},[254,451,452],{},"\u002Fhome\u002Fbitrix\u002Fwww"," (BitrixVM)",[354,455,456],{},[254,457,458],{},"\u002Fhome\u002Fbitrix\u002Fsh_import",[339,460,461,466],{},[354,462,463],{},[254,464,465],{},"\u002Fvar\u002Fwww\u002Fportal",[354,467,468],{},[254,469,470],{},"\u002Fvar\u002Fwww\u002Fsh_import",[244,472,473,474,477,478,481,482,485,486,489,490,493,494,497],{},"Внутри: ",[254,475,476],{},"\u003Cкод>\u002F"," — файлы на импорт, ",[254,479,480],{},"copy\u002F\u003Cкод>\u002F"," — архив, ",[254,483,484],{},"problem\u002F\u003Cкод>\u002F"," —\nфайлы с проблемой (",[254,487,488],{},"Sync\\FromFile\\AFileProcess::getImportFolder()",",\n",[254,491,492],{},"getDoneFolder()",", ",[254,495,496],{},"getProblemFolder()",").",[244,499,500,501,504],{},"В выгрузках — цены, клиенты, заказы. Вне корня сайта веб-сервер их не отдаёт,\nнастраивать для этого ничего не нужно. До 2.0.0 каталог был ",[254,502,503],{},"\u002Fupload\u002Fimport",",\nпод корнем сайта, а имена архива — из кода импорта и даты с точностью до\nминуты: перебором за срок хранения выгрузку скачивал кто угодно.",[506,507,509],"h3",{"id":508},"свой-каталог","Свой каталог",[244,511,512,513,516],{},"Проект задаёт каталог в ",[254,514,515],{},"\u002Fbitrix\u002F.settings_extra.php",":",[518,519,523],"pre",{"className":520,"code":521,"language":522,"meta":105,"style":105},"language-php shiki shiki-themes material-theme-lighter github-light github-dark","return [\n    'shef.insync' => [\n        'value' => [\n            'importDir' => '\u002Fvar\u002Fdata\u002Fimport',\n        ],\n        'readonly' => true,\n    ],\n];\n","php",[254,524,525,533,539,545,551,557,563,569],{"__ignoreMap":105},[526,527,530],"span",{"class":528,"line":529},"line",1,[526,531,532],{},"return [\n",[526,534,536],{"class":528,"line":535},2,[526,537,538],{},"    'shef.insync' => [\n",[526,540,542],{"class":528,"line":541},3,[526,543,544],{},"        'value' => [\n",[526,546,548],{"class":528,"line":547},4,[526,549,550],{},"            'importDir' => '\u002Fvar\u002Fdata\u002Fimport',\n",[526,552,554],{"class":528,"line":553},5,[526,555,556],{},"        ],\n",[526,558,560],{"class":528,"line":559},6,[526,561,562],{},"        'readonly' => true,\n",[526,564,566],{"class":528,"line":565},7,[526,567,568],{},"    ],\n",[526,570,572],{"class":528,"line":571},8,[526,573,574],{},"];\n",[244,576,577,578,581,582,585],{},"Принимается ",[269,579,580],{},"только абсолютный путь",". Относительный зависел бы от текущего\nкаталога процесса: агент из cron искал бы файлы не там, куда их положила\nстраница загрузки. Что-то кроме абсолютного пути — каталог по умолчанию.\nКаталог обязан быть ",[269,583,584],{},"вне корня сайта"," — модуль это не проверяет, это решение\nпроекта.",[244,587,588,589,592],{},"Модуль-импорт, которому нужен свой путь, по-прежнему может переопределить\n",[254,590,591],{},"getImportFolder()"," и соседей.",[506,594,596],{"id":595},"права-и-open_basedir","Права и open_basedir",[244,598,599,600,603,604,607],{},"Каталоги создаются сами при первом импорте — если пользователь PHP может\nписать в родителя. На BitrixVM ",[254,601,602],{},"\u002Fhome\u002Fbitrix"," принадлежит ",[254,605,606],{},"bitrix",", всё\nработает из коробки. В другом окружении создайте каталог заранее:",[518,609,613],{"className":610,"code":611,"language":612,"meta":105,"style":105},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","sudo mkdir \u002Fvar\u002Fwww\u002Fsh_import && sudo chown www-data: \u002Fvar\u002Fwww\u002Fsh_import\n","bash",[254,614,615],{"__ignoreMap":105},[526,616,617,621,625,628,632,635,638,641],{"class":528,"line":529},[526,618,620],{"class":619},"sbgvK","sudo",[526,622,624],{"class":623},"s_sjI"," mkdir",[526,626,627],{"class":623}," \u002Fvar\u002Fwww\u002Fsh_import",[526,629,631],{"class":630},"sP7_E"," &&",[526,633,634],{"class":619}," sudo",[526,636,637],{"class":623}," chown",[526,639,640],{"class":623}," www-data:",[526,642,643],{"class":623}," \u002Fvar\u002Fwww\u002Fsh_import\n",[244,645,646,647,650],{},"Если в PHP задан ",[254,648,649],{},"open_basedir",", каталог импорта должен в него входить.",[244,652,653,654,657,658,661,662,305],{},"Внешний обмен (1С, FTP, rsync), который кладёт файлы, пишет теперь ",[269,655,656],{},"сюда",", а\nне в ",[254,659,660],{},"\u002Fupload\u002Fimport\u002F\u003Cкод>\u002F",": пользователю обмена нужен доступ на запись в\n",[254,663,664],{},"\u003Cкаталог импорта>\u002F\u003Cкод>\u002F",[506,666,668],{"id":667},"после-обновления-с-1x","После обновления с 1.x",[244,670,671,672,674,675,678],{},"Старый ",[254,673,503],{}," модуль не трогает: в нём ваши данные, и он\n",[269,676,677],{},"по-прежнему открыт"," веб-серверу. Перенесите нужное, перенастройте обмены на\nновый каталог и удалите старый:",[518,680,682],{"className":610,"code":681,"language":612,"meta":105,"style":105},"mkdir -p \u002Fhome\u002Fbitrix\u002Fsh_import\ncp -a \u002Fhome\u002Fbitrix\u002Fwww\u002Fupload\u002Fimport\u002F. \u002Fhome\u002Fbitrix\u002Fsh_import\u002F\n# перенастроить обмены на \u002Fhome\u002Fbitrix\u002Fsh_import\u002F\u003Cкод>\u002F\nrm -r \u002Fhome\u002Fbitrix\u002Fwww\u002Fupload\u002Fimport\n",[254,683,684,696,710,716],{"__ignoreMap":105},[526,685,686,689,693],{"class":528,"line":529},[526,687,688],{"class":619},"mkdir",[526,690,692],{"class":691},"stzsN"," -p",[526,694,695],{"class":623}," \u002Fhome\u002Fbitrix\u002Fsh_import\n",[526,697,698,701,704,707],{"class":528,"line":535},[526,699,700],{"class":619},"cp",[526,702,703],{"class":691}," -a",[526,705,706],{"class":623}," \u002Fhome\u002Fbitrix\u002Fwww\u002Fupload\u002Fimport\u002F.",[526,708,709],{"class":623}," \u002Fhome\u002Fbitrix\u002Fsh_import\u002F\n",[526,711,712],{"class":528,"line":541},[526,713,715],{"class":714},"sutJx","# перенастроить обмены на \u002Fhome\u002Fbitrix\u002Fsh_import\u002F\u003Cкод>\u002F\n",[526,717,718,721,724],{"class":528,"line":547},[526,719,720],{"class":619},"rm",[526,722,723],{"class":691}," -r",[526,725,726],{"class":623}," \u002Fhome\u002Fbitrix\u002Fwww\u002Fupload\u002Fimport\n",[506,728,730],{"id":729},"что-ещё-держит-модуль","Что ещё держит модуль",[259,732,733,773],{},[262,734,735,738,739,742,743,493,746,489,749,493,752,493,755,493,758,493,761,764,765,768,769,772],{},[269,736,737],{},"имя загружаемого файла"," проверяет\n",[254,740,741],{},"ShefInSyncImportFromFileComponent::prepareUploadName()",": только имя без\nпути, без скрытых файлов и исполняемых расширений (",[254,744,745],{},".php",[254,747,748],{},".phtml",[254,750,751],{},".phar",[254,753,754],{},".htaccess",[254,756,757],{},".html",[254,759,760],{},".svg",[254,762,763],{},".js"," и прочие, плюс список ядра\n",[254,766,767],{},"HasScriptExtension()","), и если импорт объявил ",[254,770,771],{},"getImportFileAccept()"," с\nрасширениями — только с ними. До 2.0.0 имя от браузера шло в путь как есть;",[262,774,775,778,779,782],{},[269,776,777],{},"имена архивных файлов не угадать",": ",[254,780,781],{},"done_\u003Cкод>_\u003Cдата>_\u003C16 случайных символов>.\u003Cрасш>"," — вторая линия на случай, если проект задаст свой каталог\nпод корнем сайта.",[247,784,786],{"id":785},"sql","SQL",[244,788,789,792,793,796,797,800,801,804,805,305],{},[254,790,791],{},"Sync\\Model\\SyncCollection"," собирает запросы к таблице импорта сам. Значения —\nкод импорта, дата загрузки — экранируются через ",[254,794,795],{},"SqlHelper::forSql()","\n(",[254,798,799],{},"SyncCollection::buildWhere()","). До 2.0.0 код импорта вставлялся в запрос как\nесть, а в ",[254,802,803],{},"clear()"," он приходит параметром ajax-запроса компонента статистики:\nSQL-инъекция для любого вошедшего. Сторожит ",[254,806,807],{},"tests\u002Fsynccollection_test.php",[247,809,811],{"id":810},"строка-агента","Строка агента",[244,813,814,815,818,819,822,823,826,827,305],{},"Ядро исполняет строку агента из ",[254,816,817],{},"b_agent"," как PHP-код.\n",[254,820,821],{},"Agents\\Entity::prepareNameForDb()"," экранирует параметры ",[254,824,825],{},"var_export()",":\nкавычка в значении не ломает агент и не становится кодом. Параметры — только\nстроки и числа. Сторожит ",[254,828,829],{},"tests\u002Fagents_test.php",[247,831,833],{"id":832},"вывод","Вывод",[244,835,836,837,839,840,493,843,846],{},"Всё, что приходит из данных — строки файла, ответы API, имена агентов из\n",[254,838,817],{},", код импорта в гриде, — экранируется перед выводом. Описания\nимпорта и агента (",[254,841,842],{},"getProcessDescription()",[254,844,845],{},"Entity::getDescription()",")\nвыводятся как разметка: их пишет разработчик класса, а не пользователь.",[247,848,850],{"id":849},"логи","Логи",[244,852,853,856,857,493,860,493,863,493,866,493,869,493,872,493,875,878,879,882],{},[254,854,855],{},"Api\\AConnector"," при ошибке пишет в лог отправленный запрос. Заголовки с\n",[254,858,859],{},"authorization",[254,861,862],{},"token",[254,864,865],{},"key",[254,867,868],{},"secret",[254,870,871],{},"password",[254,873,874],{},"cookie",[254,876,877],{},"session"," в\nимени уходят туда маской (",[254,880,881],{},"Api\\Headers::mask()","). Параметры запроса пишутся\nкак есть — не кладите секреты в параметры, передавайте их заголовком.",[244,884,885,886,305],{},"Сами логи — модуля shef.problems, вне корня сайта, см. его\n",[887,888,892],"a",{"href":889,"rel":890},"https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fdocs\u002Fsecurity.md",[891],"nofollow","security.md",[247,894,896],{"id":895},"xml","XML",[244,898,899,900,903,904,907,908,305],{},"Разбор идёт через ",[254,901,902],{},"XMLReader"," без ",[254,905,906],{},"LIBXML_NOENT",": внешние сущности не\nраскрываются (XXE). Сторожит ",[254,909,910],{},"tests\u002Fvendor_test.php",[244,912,913,916,917],{},[887,914,915],{"href":229},"← Опции настроек модуля"," | ",[887,918,919],{"href":181},"↑ Содержание",[921,922,923],"note",{},[244,924,925,926,931],{},"Источник: ",[887,927,930],{"href":928,"rel":929},"https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Finsync\u002Fblob\u002Fmain\u002Fdocs\u002Fsecurity.md",[891],"insync\u002Fdocs\u002Fsecurity.md"," — правки туда, сайт пересобирается сам.",[933,934,935],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}",{"title":105,"searchDepth":541,"depth":541,"links":937},[938,939,945,946,947,948,949],{"id":249,"depth":535,"text":250},{"id":419,"depth":535,"text":420,"children":940},[941,942,943,944],{"id":508,"depth":541,"text":509},{"id":595,"depth":541,"text":596},{"id":667,"depth":541,"text":668},{"id":729,"depth":541,"text":730},{"id":785,"depth":535,"text":786},{"id":810,"depth":535,"text":811},{"id":832,"depth":535,"text":833},{"id":849,"depth":535,"text":850},{"id":895,"depth":535,"text":896},"md",{},true,"---\ntitle: \"Безопасность\"\n---\n\nМодуль даёт другим модулям заготовки для синхронизаций, и самые опасные места\nу него общие с ними: кто может запускать агенты и импорт, что ложится в каталоги\nимпорта, что уходит в SQL и в лог. Ниже — что держит модуль и что остаётся\nпроекту.\n\n## Кто управляет импортом\n\n`\\Shef\\InSync\\Main\\Access::canManage()` — одно правило на весь модуль:\n\n* администратор портала — да;\n* пользователь с правом **«Запись» (W)** и выше на **модуль импорта** в\n  «Настройки → Настройки продукта → Права доступа» — да;\n* остальные, включая гостя, — нет.\n\nМодуль импорта — тот, чей агент или класс импорта: права на `shef.demosync`\nоткрывают его импорт, но не агенты `main`. Трогать из интерфейса модуля можно\nтолько агенты импорта — наследники `\\Shef\\InSync\\Agents\\AAgent`: право «W» на\n`sale` или `crm` не открывает их штатные агенты, это остаётся администратору\nв списке агентов ядра. Для страницы статистики и перехода\nк таблице импорта — права на `shef.insync`.\n\nПроверяется **и на показ, и в действии**: адрес ajax-действия виден в коде\nстраницы и вызывается напрямую. До 2.0.0 все действия модуля стояли на\n`Actions\\Normal` — это умолчания ядра, вход на портал и csrf, — и любой\nвошедший сотрудник:\n\n* включал и выключал **любой агент портала** по ID — и из компонента\n  статистики, и из контроллера страницы настроек;\n* загружал файлы в импорт и прогонял агент импорта;\n* чистил таблицу импорта.\n\n| где | что проверяется |\n|---|---|\n| левое меню (`Integration\\Intranet\\CustomSectionProvider`) | страницы модуля видны только тем, кому разрешены |\n| `shef.insync:import.stat.local` | страница и все действия — права на `shef.insync`; кнопки агента — права на модуль агента |\n| `shef.insync:import.from.file` | страница и действия — права на модуль импорта, **до** того как подключается модуль и создаётся объект импорта |\n| `Main\\Options\\Agent\\Controller` | агент существует, принадлежит модулю из запроса, права на этот модуль |\n| `Main\\Options\\Import\\FromFile\\AController` | права на модуль контроллера (из его namespace) |\n\nСторожит `tests\u002Faccess_test.php`.\n\n## Каталоги импорта — вне корня сайта\n\nКаталог импорта — `\\Shef\\InSync\\Main\\Constants::getImportDir()`. По\nумолчанию он на уровень **выше** корня сайта:\n\n| корень сайта | каталог импорта |\n|---|---|\n| `\u002Fhome\u002Fbitrix\u002Fwww` (BitrixVM) | `\u002Fhome\u002Fbitrix\u002Fsh_import` |\n| `\u002Fvar\u002Fwww\u002Fportal` | `\u002Fvar\u002Fwww\u002Fsh_import` |\n\nВнутри: `\u003Cкод>\u002F` — файлы на импорт, `copy\u002F\u003Cкод>\u002F` — архив, `problem\u002F\u003Cкод>\u002F` —\nфайлы с проблемой (`Sync\\FromFile\\AFileProcess::getImportFolder()`,\n`getDoneFolder()`, `getProblemFolder()`).\n\nВ выгрузках — цены, клиенты, заказы. Вне корня сайта веб-сервер их не отдаёт,\nнастраивать для этого ничего не нужно. До 2.0.0 каталог был `\u002Fupload\u002Fimport`,\nпод корнем сайта, а имена архива — из кода импорта и даты с точностью до\nминуты: перебором за срок хранения выгрузку скачивал кто угодно.\n\n### Свой каталог\n\nПроект задаёт каталог в `\u002Fbitrix\u002F.settings_extra.php`:\n\n```php\nreturn [\n\t'shef.insync' => [\n\t\t'value' => [\n\t\t\t'importDir' => '\u002Fvar\u002Fdata\u002Fimport',\n\t\t],\n\t\t'readonly' => true,\n\t],\n];\n```\n\nПринимается **только абсолютный путь**. Относительный зависел бы от текущего\nкаталога процесса: агент из cron искал бы файлы не там, куда их положила\nстраница загрузки. Что-то кроме абсолютного пути — каталог по умолчанию.\nКаталог обязан быть **вне корня сайта** — модуль это не проверяет, это решение\nпроекта.\n\nМодуль-импорт, которому нужен свой путь, по-прежнему может переопределить\n`getImportFolder()` и соседей.\n\n### Права и open_basedir\n\nКаталоги создаются сами при первом импорте — если пользователь PHP может\nписать в родителя. На BitrixVM `\u002Fhome\u002Fbitrix` принадлежит `bitrix`, всё\nработает из коробки. В другом окружении создайте каталог заранее:\n\n```bash\nsudo mkdir \u002Fvar\u002Fwww\u002Fsh_import && sudo chown www-data: \u002Fvar\u002Fwww\u002Fsh_import\n```\n\nЕсли в PHP задан `open_basedir`, каталог импорта должен в него входить.\n\nВнешний обмен (1С, FTP, rsync), который кладёт файлы, пишет теперь **сюда**, а\nне в `\u002Fupload\u002Fimport\u002F\u003Cкод>\u002F`: пользователю обмена нужен доступ на запись в\n`\u003Cкаталог импорта>\u002F\u003Cкод>\u002F`.\n\n### После обновления с 1.x\n\nСтарый `\u002Fupload\u002Fimport` модуль не трогает: в нём ваши данные, и он\n**по-прежнему открыт** веб-серверу. Перенесите нужное, перенастройте обмены на\nновый каталог и удалите старый:\n\n```bash\nmkdir -p \u002Fhome\u002Fbitrix\u002Fsh_import\ncp -a \u002Fhome\u002Fbitrix\u002Fwww\u002Fupload\u002Fimport\u002F. \u002Fhome\u002Fbitrix\u002Fsh_import\u002F\n# перенастроить обмены на \u002Fhome\u002Fbitrix\u002Fsh_import\u002F\u003Cкод>\u002F\nrm -r \u002Fhome\u002Fbitrix\u002Fwww\u002Fupload\u002Fimport\n```\n\n### Что ещё держит модуль\n\n* **имя загружаемого файла** проверяет\n  `ShefInSyncImportFromFileComponent::prepareUploadName()`: только имя без\n  пути, без скрытых файлов и исполняемых расширений (`.php`, `.phtml`,\n  `.phar`, `.htaccess`, `.html`, `.svg`, `.js` и прочие, плюс список ядра\n  `HasScriptExtension()`), и если импорт объявил `getImportFileAccept()` с\n  расширениями — только с ними. До 2.0.0 имя от браузера шло в путь как есть;\n* **имена архивных файлов не угадать**: `done_\u003Cкод>_\u003Cдата>_\u003C16 случайных\n  символов>.\u003Cрасш>` — вторая линия на случай, если проект задаст свой каталог\n  под корнем сайта.\n\n## SQL\n\n`Sync\\Model\\SyncCollection` собирает запросы к таблице импорта сам. Значения —\nкод импорта, дата загрузки — экранируются через `SqlHelper::forSql()`\n(`SyncCollection::buildWhere()`). До 2.0.0 код импорта вставлялся в запрос как\nесть, а в `clear()` он приходит параметром ajax-запроса компонента статистики:\nSQL-инъекция для любого вошедшего. Сторожит `tests\u002Fsynccollection_test.php`.\n\n## Строка агента\n\nЯдро исполняет строку агента из `b_agent` как PHP-код.\n`Agents\\Entity::prepareNameForDb()` экранирует параметры `var_export()`:\nкавычка в значении не ломает агент и не становится кодом. Параметры — только\nстроки и числа. Сторожит `tests\u002Fagents_test.php`.\n\n## Вывод\n\nВсё, что приходит из данных — строки файла, ответы API, имена агентов из\n`b_agent`, код импорта в гриде, — экранируется перед выводом. Описания\nимпорта и агента (`getProcessDescription()`, `Entity::getDescription()`)\nвыводятся как разметка: их пишет разработчик класса, а не пользователь.\n\n## Логи\n\n`Api\\AConnector` при ошибке пишет в лог отправленный запрос. Заголовки с\n`authorization`, `token`, `key`, `secret`, `password`, `cookie`, `session` в\nимени уходят туда маской (`Api\\Headers::mask()`). Параметры запроса пишутся\nкак есть — не кладите секреты в параметры, передавайте их заголовком.\n\nСами логи — модуля shef.problems, вне корня сайта, см. его\n[security.md](https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fdocs\u002Fsecurity.md).\n\n## XML\n\nРазбор идёт через `XMLReader` без `LIBXML_NOENT`: внешние сущности не\nраскрываются (XXE). Сторожит `tests\u002Fvendor_test.php`.\n\n[← Опции настроек модуля](\u002Fmodules\u002Finsync\u002Foptions) | [↑ Содержание](\u002Fmodules\u002Finsync)\n\n::note\nИсточник: [insync\u002Fdocs\u002Fsecurity.md](https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Finsync\u002Fblob\u002Fmain\u002Fdocs\u002Fsecurity.md) — правки туда, сайт пересобирается сам.\n::\n\n",{"title":199,"description":202},"-5EGifFTeb2Gf1e569I-YqrZPABFcsNLH6aFQJ-7ZJ4",[957,958],{"title":92,"path":195,"stem":196,"description":197,"children":-1},{"title":204,"path":205,"stem":206,"description":207,"children":-1},1790857269864]