Skip to content

refactor(opslogexport): pass ops log via fd to eliminate path exposure - #483

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:release/eaglefrom
GongHeng2017:202707031544-re-eagle-fix
Jul 3, 2026
Merged

refactor(opslogexport): pass ops log via fd to eliminate path exposure#483
deepin-bot[bot] merged 1 commit into
linuxdeepin:release/eaglefrom
GongHeng2017:202707031544-re-eagle-fix

Conversation

@GongHeng2017

Copy link
Copy Markdown
Contributor

Rework ops log export to use a caller-provided fd: the frontend opens a zip file for writing and passes its fd over D-Bus; the root service collects logs under a /var/log temp dir, zips them, writes to the fd, and cleans up itself, never returning a path to the caller.

重构运维日志导出:前端以写方式打开压缩包文件并通过 D-Bus 传入 fd,
root 服务在 /var/log 临时目录收集日志、整体压缩后写入该 fd 并自行清理,
全程不向调用方暴露 /var/log 路径,也无需前端再调用清理接口。

  • Remove removeOpsLogTempDir() and the m_opsTempDirs caller-cached map.
  • Drop setDirectoryPermissionsSafe() (chgrp/0750/0640), as the frontend no longer reads the temp dir directly, closing the read window.
  • Add zip step in service; frontend unzips the received archive.
  • Update D-Bus introspection xml to exportOpsLog(in h fd, out b).

Log: 重构运维日志导出为 fd 直传,移除路径返回与清理接口
Influence: 运维日志导出不再向调用方返回 /var/log 临时目录路径,root 服务在单次 D-Bus 调用内完成收集、压缩、回传与清理,消除路径替换/越权删除攻击面及前端拷贝时间窗的越权读取风险,导出功能对最终用户行为保持不变。

Rework ops log export to use a caller-provided fd: the frontend opens
a zip file for writing and passes its fd over D-Bus; the root service
collects logs under a /var/log temp dir, zips them, writes to the fd,
and cleans up itself, never returning a path to the caller.

重构运维日志导出:前端以写方式打开压缩包文件并通过 D-Bus 传入 fd,
root 服务在 /var/log 临时目录收集日志、整体压缩后写入该 fd 并自行清理,
全程不向调用方暴露 /var/log 路径,也无需前端再调用清理接口。

- Remove removeOpsLogTempDir() and the m_opsTempDirs caller-cached map.
- Drop setDirectoryPermissionsSafe() (chgrp/0750/0640), as the frontend
  no longer reads the temp dir directly, closing the read window.
- Add zip step in service; frontend unzips the received archive.
- Update D-Bus introspection xml to exportOpsLog(in h fd, out b).

Log: 重构运维日志导出为 fd 直传,移除路径返回与清理接口
Influence: 运维日志导出不再向调用方返回 /var/log 临时目录路径,root 服务在单次 D-Bus 调用内完成收集、压缩、回传与清理,消除路径替换/越权删除攻击面及前端拷贝时间窗的越权读取风险,导出功能对最终用户行为保持不变。

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @GongHeng2017, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
  • 检测到敏感词export变动
详情
    {
    "export": {
        "application/assets/com.deepin.pkexec.logViewerAuth.policy": {
            "a": [
                "\t\t<message xml:lang=\"fr\">L'authentification est requise pour exporter les journaux</message>",
                "\t\t<message xml:lang=\"pt\">\u00c9 necess\u00e1ria autentica\u00e7\u00e3o para exportar registos</message>",
                "\t\t<message xml:lang=\"ca\">Cal autenticaci\u00f3 per exportar registres.</message>",
                "\t\t<message xml:lang=\"es\">Se requiere autenticaci\u00f3n para exportar registros</message>",
                "\t\t<message xml:lang=\"hu\">Hiteles\u00edt\u00e9s sz\u00fcks\u00e9ges a napl\u00f3k export\u00e1l\u00e1s\u00e1hoz</message>",
                "\t\t<message xml:lang=\"fr\">L'authentification est requise pour exporter les journaux</message>",
                "\t\t<message xml:lang=\"pt\">\u00c9 necess\u00e1ria autentica\u00e7\u00e3o para exportar registos</message>",
                "\t\t<message xml:lang=\"ca\">Cal autenticaci\u00f3 per exportar registres.</message>",
                "\t\t<message xml:lang=\"es\">Se requiere autenticaci\u00f3n para exportar registros</message>",
                "\t\t<message xml:lang=\"hu\">Hiteles\u00edt\u00e9s sz\u00fcks\u00e9ges a napl\u00f3k export\u00e1l\u00e1s\u00e1hoz</message>"
            ],
            "b": [
                "\t\t<message xml:lang=\"hu\">Hiteles\u00edt\u00e9s sz\u00fcks\u00e9ges a napl\u00f3k export\u00e1l\u00e1s\u00e1hoz</message>",
                "\t\t<message xml:lang=\"pt\">\u00c9 necess\u00e1ria autentica\u00e7\u00e3o para exportar registos</message>",
                "\t\t<message xml:lang=\"fr\">L'authentification est requise pour exporter les journaux</message>",
                "\t\t<message xml:lang=\"es\">Se requiere autenticaci\u00f3n para exportar registros</message>",
                "\t\t<message xml:lang=\"ca\">Cal autenticaci\u00f3 per exportar registres.</message>",
                "\t\t<message xml:lang=\"hu\">Hiteles\u00edt\u00e9s sz\u00fcks\u00e9ges a napl\u00f3k export\u00e1l\u00e1s\u00e1hoz</message>",
                "\t\t<message xml:lang=\"pt\">\u00c9 necess\u00e1ria autentica\u00e7\u00e3o para exportar registos</message>",
                "\t\t<message xml:lang=\"fr\">L'authentification est requise pour exporter les journaux</message>",
                "\t\t<message xml:lang=\"es\">Se requiere autenticaci\u00f3n para exportar registros</message>",
                "\t\t<message xml:lang=\"ca\">Cal autenticaci\u00f3 per exportar registres.</message>"
            ]
        },
        "application/dbusproxy/dldbushandler.cpp": {
            "a": [
                "QString DLDBusHandler::exportOpsLog()",
                "    QDBusPendingReply<QString> reply = m_dbus->exportOpsLog();",
                "        qCritical(logDBusHandler) << \"call dbus interface 'exportOpsLog' failed. error info:\" << reply.error().message();",
                "        qCDebug(logDBusHandler) << \"exportOpsLog succeeded, root temp dir:\" << reply.value();"
            ],
            "b": [
                "bool DLDBusHandler::exportOpsLog(const QString &zipFilePath)",
                "        qCritical(logDBusHandler) << \"exportOpsLog: failed to open zip file for writing:\" << zipFilePath",
                "        qCritical(logDBusHandler) << \"exportOpsLog: invalid file descriptor for:\" << zipFilePath;",
                "    QDBusPendingReply<bool> reply = m_dbus->exportOpsLog(dbusFd);",
                "        qCritical(logDBusHandler) << \"call dbus interface 'exportOpsLog' failed. error info:\" << reply.error().message();",
                "    qCDebug(logDBusHandler) << \"exportOpsLog finished, result:\" << ok << \"zip:\" << zipFilePath;"
            ]
        },
        "application/dbusproxy/dldbushandler.h": {
            "a": [
                "    QString exportOpsLog();"
            ],
            "b": [
                "    bool exportOpsLog(const QString &zipFilePath);"
            ]
        },
        "application/dbusproxy/dldbusinterface.h": {
            "a": [
                "    inline QDBusPendingReply<QString> exportOpsLog()"
            ],
            "b": [
                "    inline QDBusPendingReply<bool> exportOpsLog(const QDBusUnixFileDescriptor &fd)"
            ]
        },
        "application/logallexportthread.cpp": {
            "a": [
                "        QString rootTempDir = DLDBusHandler::instance(this)->exportOpsLog();",
                "            qCWarning(logExportAll) << \"exportOpsLog returned empty or non-existent root temp dir, root-side ops logs may be missing\";"
            ],
            "b": [
                "        bool opsOk = DLDBusHandler::instance(this)->exportOpsLog(opsZipPath);",
                "            qCWarning(logExportAll) << \"exportOpsLog failed or zip not produced, root-side ops logs may be missing\";"
            ]
        },
        "logViewerService/logviewerservice.cpp": {
            "a": [
                "QString LogViewerService::exportOpsLog()",
                "    // \u6388\u6743\u6821\u9a8c\uff1a\u4e0e exportOpsLog \u4e00\u81f4\uff0c\u4f7f\u7528 s_Action_View\u3002",
                "    // \u65e0\u53c2\u8bbe\u8ba1\uff1a\u6e05\u7406\u7684\u662f\u670d\u52a1\u7aef\u5728 exportOpsLog \u4e2d\u6309\u5f53\u524d\u8c03\u7528\u8005\u7f13\u5b58\u7684\u672c\u8fdb\u7a0b\u4e34\u65f6\u76ee\u5f55\u8def\u5f84\uff0c"
            ],
            "b": [
                "bool LogViewerService::exportOpsLog(const QDBusUnixFileDescriptor &fd)",
                "        qCWarning(logService) << \"exportOpsLog: invalid file descriptor from caller\";",
                "        qCWarning(logService) << \"exportOpsLog: failed to create temp zip file:\" << tmpZipFile.errorString();",
                "        qCWarning(logService) << \"exportOpsLog: zip failed, exitCode:\" << zipProc.exitCode()",
                "            qCWarning(logService) << \"exportOpsLog: failed to open temp zip for reading:\" << tmpZipPath;",
                "                qCWarning(logService) << \"exportOpsLog: failed to open caller fd for writing\";",
                "                    qCWarning(logService) << \"exportOpsLog: write to caller fd failed\";",
                "        qCWarning(logService) << \"exportOpsLog: aborted, failed to write zip to caller fd\";",
                "    qCDebug(logService) << \"exportOpsLog: ops logs zipped and written to caller fd successfully\";",
                "// \u57fa\u4e8e fd \u7684 TOCTOU \u5b89\u5168\u65b9\u5f0f\u5220\u9664 exportOpsLog \u4ea7\u751f\u7684 /var/log \u4e34\u65f6\u76ee\u5f55\uff08opsDir\uff09\u3002",
                "// \u7531 exportOpsLog \u5728\u5199\u5165 fd \u5b8c\u6210\u540e\u81ea\u52a8\u8c03\u7528\uff0c\u786e\u4fdd /var/log \u4e0b\u4e0d\u6b8b\u7559\u542b\u7cfb\u7edf\u65e5\u5fd7\u7684\u76ee\u5f55\u3002"
            ]
        },
        "logViewerService/logviewerservice.h": {
            "a": [
                "    Q_SCRIPTABLE QString exportOpsLog();",
                "    // \u6e05\u7406 exportOpsLog \u5728 /var/log \u4e0b\u521b\u5efa\u7684\u4e34\u65f6\u5bfc\u51fa\u76ee\u5f55\u3002\u65e0\u53c2\uff1a\u6e05\u7406\u7684\u662f\u670d\u52a1\u7aef\u7f13\u5b58",
                "    // exportOpsLog \u521b\u5efa\u7684 /var/log \u4e34\u65f6\u76ee\u5f55\u8def\u5f84\uff0c\u6309 D-Bus \u8c03\u7528\u65b9 unique bus name \u9694\u79bb\u5b58\u50a8\uff0c",
                "    // \u6309 fd-relative \u5b89\u5168\u65b9\u5f0f\u5220\u9664 exportOpsLog \u4ea7\u751f\u7684 /var/log \u4e34\u65f6\u76ee\u5f55\uff08path \u4e3a\u7f13\u5b58\u8def\u5f84\uff09\u3002"
            ],
            "b": [
                "    Q_SCRIPTABLE bool exportOpsLog(const QDBusUnixFileDescriptor &fd);",
                "    // \u57fa\u4e8e fd \u7684 TOCTOU \u5b89\u5168\u65b9\u5f0f\u5220\u9664 exportOpsLog \u4ea7\u751f\u7684 /var/log \u4e34\u65f6\u76ee\u5f55\uff08opsDir\uff09\u3002",
                "    // \u7531 exportOpsLog \u5199\u5165 fd \u5b8c\u6210\u540e\u81ea\u52a8\u8c03\u7528\u56de\u6536\uff0c\u6210\u529f\u8fd4\u56de true\u3002"
            ]
        },
        "logViewerService/opslogexport.cpp": {
            "a": [
                "    // - 0750/0640 \u800c\u975e 0755/0644\uff1a\u4ec5 caller \u540c\u7ec4\u53ef\u8bfb\uff0c\u907f\u514d\u4efb\u610f\u672c\u5730\u7528\u6237\u5728 exportOpsLog",
                "            qWarning() << \"Failed to chgrp export path:\" << path << \"error:\" << strerror(errno);"
            ]
        }
    }
}

@github-actions
github-actions Bot requested a review from liujianqiang-niu July 3, 2026 09:30
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:95分

■ 【总体评价】

代码重构了运维日志导出流程,通过文件描述符传递数据消除了路径暴露风险,但存在国际化文本缺失问题
逻辑正确且安全性大幅提升,因部分语种缺失提示信息扣5分

■ 【详细分析】

  • 1.语法逻辑(完全正确)✓

具体分析内容:dldbushandler.cpp中exportOpsLog函数正确创建QFile并获取句柄,通过QDBusUnixFileDescriptor传递给后端;logviewerservice.cpp中后端正确接收fd,使用1MB缓冲区循环读取临时zip文件并写入fd,最后清理临时目录和zip文件。QFile::remove(tmpZipPath)在zip命令调用前删除空文件,避免了zip命令报错退出,逻辑严密。
建议:无

  • 2.代码质量(良好)✓

具体分析内容:代码注释非常详尽,清晰解释了为何删除空文件、为何不再需要修改权限等设计决策。移除了m_opsTempDirs成员变量和removeOpsLogTempDir接口,简化了类结构。但在com.deepin.pkexec.logViewerAuth.policy和com.deepin.pkexec.logViewerTruncate.policy文件中,部分语种(如da、gl_ES、sr、sl、hr、ar、it、br、en_GB、ms)仅提供了标签却缺失对应的标签。
潜在问题:导致使用这些语种的系统在触发PolicyKit认证时,对话框中无法显示本地化的提示信息,回退到默认英文或显示为空,影响用户体验。
建议:补全所有存在标签的语种对应的标签内容,保持国际化资源的完整性。

  • 3.代码性能(高效)✓

具体分析内容:后端写入fd时使用了1MB的缓冲区(constexpr qint64 bufSize = 1 << 20),相比小缓冲区减少了系统调用次数。整体流程从“root创建目录->前端cp拷贝->root删除目录”优化为“root打包直接写入前端fd”,减少了跨权限边界的文件系统遍历和IO操作。
建议:无

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
本次修改是一次重大的安全加固,彻底消除了旧方案中root临时目录路径暴露给普通用户前端的时间窗攻击面。通过D-Bus的SCM_RIGHTS机制传递文件描述符,后端在root权限下完成收集、压缩、回传、清理的闭环,不再需要将/var/log下的目录权限降级给普通用户,杜绝了符号链接跟随和权限越权风险。后端删除临时目录依然保持了基于fd的TOCTOU安全实现。

  • 建议:保持当前安全的fd传递架构设计

■ 【改进建议代码示例】

<!-- 修复 com.deepin.pkexec.logViewerAuth.policy 中缺失的丹麦语 message -->
<!-- 其他缺失的语种(如 gl_ES, sr, sl 等)需按相同模式补全 -->
<description xml:lang="da">autentifikation</description>
<message xml:lang="da">Autentifikation er påkrævet for at se loggen</message>

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: GongHeng2017, max-lvs

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@GongHeng2017

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

This pr force merged! (status: unstable)

@deepin-bot
deepin-bot Bot merged commit e3351fd into linuxdeepin:release/eagle Jul 3, 2026
21 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants