You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Snyk检测到的问题自动创建Jira工单?

自动化Snyk漏洞同步到Jira工单方案

前置准备

  • 生成并保存核心密钥:
    • Snyk API Token:在Snyk账号设置的「API Tokens」区域生成
    • Jira API Token(云版):在Atlassian账号「Security settings」中生成;服务器版直接使用账号密码
  • 确认Jira基础配置:Jira域名、目标项目Key、Issue类型(如Bug或自定义Vulnerability)、必填字段(描述、优先级等)

一、实时同步新出现的Snyk漏洞

利用Snyk Webhook触发实时创建Jira工单,步骤如下:

  1. 配置Snyk Webhook
    在Snyk组织或项目的「Settings」→「Webhooks」中添加新Webhook:

    • 目标URL:你的自定义服务公网访问地址
    • 触发事件:勾选「New vulnerability introduced」「Vulnerability state changed」等需要触发的事件
  2. 编写Webhook处理服务(Python示例)
    该服务接收Snyk的Webhook payload,提取漏洞信息后调用Jira API创建工单:

    import requests
    from flask import Flask, request
    
    app = Flask(__name__)
    
    # 配置参数
    SNYK_WEBHOOK_SECRET = "你的Snyk Webhook密钥"
    JIRA_DOMAIN = "your-domain.atlassian.net"
    JIRA_API_TOKEN = "你的Jira API Token"
    JIRA_PROJECT_KEY = "PROJ"
    JIRA_ISSUE_TYPE = "Bug"
    
    @app.route('/snyk-webhook', methods=['POST'])
    def snyk_webhook_handler():
        # 验证Webhook签名(可选但推荐)
        if request.headers.get('X-Snyk-Signature') != SNYK_WEBHOOK_SECRET:
            return "Unauthorized", 403
    
        payload = request.json
        vuln = payload.get('vulnerability')
        project = payload.get('project')
    
        if not vuln or not project:
            return "Invalid payload", 400
    
        # 构建Jira工单数据
        jira_payload = {
            "fields": {
                "project": {"key": JIRA_PROJECT_KEY},
                "summary": f"Snyk漏洞:{vuln['title']}({project['name']})",
                "description": f"""
                **漏洞详情**:
                - 漏洞ID:{vuln['id']}
                - 影响包:{vuln['package']}@{vuln['version']}
                - 严重程度:{vuln['severity'].upper()}
                - 项目:{project['name']}
                - 修复建议:{vuln['fix']['description'] if vuln.get('fix') else '无'}
                """,
                "issuetype": {"name": JIRA_ISSUE_TYPE},
                "priority": {"name": "High" if vuln['severity'] in ['critical', 'high'] else "Medium"}
            }
        }
    
        # 调用Jira API创建工单
        jira_url = f"https://{JIRA_DOMAIN}/rest/api/3/issue"
        auth = ("your-jira-email@example.com", JIRA_API_TOKEN)
        response = requests.post(jira_url, json=jira_payload, auth=auth)
    
        if response.status_code == 201:
            return "Jira issue created", 200
        else:
            return f"Failed to create issue: {response.text}", 500
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    

二、批量同步已存在的Snyk漏洞

针对历史漏洞,通过Snyk API批量拉取并同步到Jira:

import requests

# 配置参数
SNYK_API_TOKEN = "你的Snyk API Token"
JIRA_DOMAIN = "your-domain.atlassian.net"
JIRA_API_TOKEN = "你的Jira API Token"
JIRA_PROJECT_KEY = "PROJ"
JIRA_ISSUE_TYPE = "Bug"

def get_snyk_projects():
    url = "https://snyk.io/api/v1/org/your-org-id/projects"
    headers = {"Authorization": f"token {SNYK_API_TOKEN}"}
    response = requests.get(url, headers=headers)
    return response.json().get('projects', [])

def get_project_vulnerabilities(project_id):
    url = f"https://snyk.io/api/v1/project/{project_id}/issues"
    headers = {"Authorization": f"token {SNYK_API_TOKEN}"}
    response = requests.get(url, headers=headers)
    return response.json().get('issues', [])

def create_jira_issue(vuln, project_name):
    # 检查漏洞是否已存在Jira工单(通过漏洞ID匹配)
    jira_search_url = f"https://{JIRA_DOMAIN}/rest/api/3/search"
    auth = ("your-jira-email@example.com", JIRA_API_TOKEN)
    search_payload = {
        "jql": f'project="{JIRA_PROJECT_KEY}" AND summary ~ "{vuln["id"]}"'
    }
    search_response = requests.post(jira_search_url, json=search_payload, auth=auth)
    if search_response.json().get('total', 0) > 0:
        print(f"漏洞{vuln['id']}已存在Jira工单,跳过")
        return

    # 构建工单数据
    jira_payload = {
        "fields": {
            "project": {"key": JIRA_PROJECT_KEY},
            "summary": f"Snyk漏洞:{vuln['title']}({project_name})",
            "description": f"""
            **漏洞详情**:
            - 漏洞ID:{vuln['id']}
            - 影响包:{vuln['package']}@{vuln['version']}
            - 严重程度:{vuln['severity'].upper()}
            - 项目:{project_name}
            - 修复建议:{vuln['fix']['description'] if vuln.get('fix') else '无'}
            """,
            "issuetype": {"name": JIRA_ISSUE_TYPE},
            "priority": {"name": "High" if vuln['severity'] in ['critical', 'high'] else "Medium"}
        }
    }

    jira_url = f"https://{JIRA_DOMAIN}/rest/api/3/issue"
    response = requests.post(jira_url, json=jira_payload, auth=auth)
    if response.status_code == 201:
        print(f"成功创建Jira工单:{response.json()['key']}")
    else:
        print(f"创建失败:{response.text}")

# 执行批量同步
projects = get_snyk_projects()
for project in projects:
    print(f"处理项目:{project['name']}")
    vulns = get_project_vulnerabilities(project['id'])
    for vuln in vulns:
        create_jira_issue(vuln, project['name'])

三、可扩展性优化

  • 去重机制:通过Jira的JQL搜索匹配漏洞ID,避免重复创建工单;也可在Snyk漏洞中标注同步状态
  • 错误重试:添加请求重试逻辑(如tenacity库)处理API调用失败场景
  • 日志记录:记录同步过程中的成功/失败日志,便于排查问题
  • 部署方式:将服务部署为Docker容器或Serverless函数(如AWS Lambda),降低运维成本
  • 字段映射:根据团队需求,将Snyk的CVSS分数、修复难度等属性映射到Jira自定义字段

内容的提问来源于stack exchange,提问作者Jonathan Gruber

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 16:05:37