利用Sonatype API实现依赖检测工具

admin 2024年11月12日20:18:59评论2 views字数 7840阅读26分8秒阅读模式

背景

由于毕设答辩时间提前,导致迫不得已砍掉部分功能,而个人感觉功能有点少故添加了个依赖检测功能,大体原理利用Maven API解析提取上传的Pom文件中的依赖项,然后借助Sonatype进行安全检测。

依赖项提取

其实这里实现方式多种,模仿XmlDecoder的方式自定义DocumentHandler虽然可以(Tomcat内部解析web.xml就是这种方式)不过对于部分把版本号以变量形式约束定义在Properties标签的情况处理比较麻烦,故采用Maven API,它可以直接提取properties中的标签值等。

利用Sonatype API实现依赖检测工具

依赖

<dependency>  <groupId>org.apache.maven</groupId>  <artifactId>maven-model-builder</artifactId>  <version>3.8.1</version></dependency><dependency>  <groupId>org.apache.maven</groupId>  <artifactId>maven-model</artifactId>   <version>3.8.1</version></dependency>

代码实现

import java.io.File;import java.io.FileReader;import java.io.StringReader;import java.io.StringWriter;import java.util.ArrayList;import java.util.List;import java.util.Properties;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import org.apache.maven.model.Dependency;import org.apache.maven.model.DependencyManagement;import org.apache.maven.model.Model;import org.apache.maven.model.io.DefaultModelWriter;import org.apache.maven.model.io.xpp3.MavenXpp3Reader;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NodeList;public class PomParser {    // 参数:Pom文件绝对路径    public static List<Dependency> parse(String pomPath) throws Exception {        List<Dependency> result = new ArrayList<>();        MavenXpp3Reader reader = new MavenXpp3Reader();        Model model = reader.read(new FileReader(pomPath));        Properties properties = model.getProperties(); // 获取所有属性        // 将model对象转为xml字符串        DefaultModelWriter writer = new DefaultModelWriter();        StringWriter stringWriter = new StringWriter();        writer.write(stringWriter, null, model);        String xmlString = stringWriter.toString();        // 合并Dependencies和DependencyManagement        List<org.apache.maven.model.Dependency> allDependencies=model.getDependencies();        DependencyManagement dependencyManagement = model.getDependencyManagement();        if (dependencyManagement!=null && dependencyManagement.getDependencies().size()>0){            allDependencies.addAll(dependencyManagement.getDependencies());        }        for (org.apache.maven.model.Dependency dependency : allDependencies) { // 遍历每个依赖对象            String version = dependency.getVersion();            if (version!=null && version.startsWith("${") && version.endsWith("}")) { // 如果版本号以${}包裹,则需要进行替换                String propertyName = version.substring(2, version.length() - 1);                String propertyValue = properties.getProperty(propertyName);                if (propertyValue == null) { // 如果属性不存在,抛出异常                    throw new IllegalArgumentException("Property not found: " + propertyName);                }                dependency.setVersion(propertyValue);            }        }        for (org.apache.maven.model.Dependency dependency : allDependencies) {            result.add(new Dependency(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()));        }        return result;    }}// Denpendency结构体    public static class Dependency {        private final String groupId;        private final String artifactId;        private final String version;        public Dependency(String groupId, String artifactId, String version) {            this.groupId = groupId;            this.artifactId = artifactId;            this.version = version;        }        public String getGroupId() {            return groupId;        }        public String getArtifactId() {            return artifactId;        }        public String getVersion() {            return version;        }    }

Https问题

由于Sonatype的API为HTTPS,导致测试时候获取不到数据,查阅网上方案得以解决

package com.VulnScanner.DpendCheck;import org.apache.http.HttpEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.ssl.NoopHostnameVerifier;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.ssl.SSLContextBuilder;import org.apache.http.ssl.TrustStrategy;import org.apache.http.util.EntityUtils;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.SSLContext;import java.io.IOException;import java.security.KeyManagementException;import java.security.KeyStoreException;import java.security.NoSuchAlgorithmException;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;public class HttpsUtils {    static CloseableHttpClient httpClient;    static CloseableHttpResponse httpResponse;    public static CloseableHttpClient createSSLClientDefault() {        try {            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {                // 信任所有                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {                    return true;                }            }).build();            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);            return HttpClients.custom().setSSLSocketFactory(sslsf).build();        } catch (KeyManagementException e) {            e.printStackTrace();        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();        } catch (KeyStoreException e) {            e.printStackTrace();        }        return HttpClients.createDefault();    }    /**     * 发送https请求     *     * @param content     * @throws Exception     */    public static String send(String content, String url) {        try {            HttpPost request = new HttpPost(url);            StringEntity entity = new StringEntity(content, "UTF-8");            entity.setContentEncoding("UTF-8");            entity.setContentType("application/json");            request.setEntity(entity);            request.addHeader("Connection", "Keep-Alive");            request.addHeader("accept", "application/json");            request.addHeader("Content-Type", "application/json");            httpClient = HttpsUtils.createSSLClientDefault();            httpResponse = httpClient.execute(request);            HttpEntity httpEntity = httpResponse.getEntity();            if (httpEntity != null) {                String jsObject = EntityUtils.toString(httpEntity, "UTF-8");                return jsObject;            } else {                return null;            }        } catch (Exception e) {            e.printStackTrace();            return null;        } finally {            try {                httpResponse.close();                httpClient.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

调用API检测

Sonatype API:https://ossindex.sonatype.org/rest

利用Sonatype API实现依赖检测工具

可以看到参数主要是这个 coordinates 字段,关于它的格式定义(https://ossindex.sonatype.org/doc/coordinates)

利用Sonatype API实现依赖检测工具

如果是检测Java项目则格式如下

maven:groupId/artifactId@Version

是以,我们可以进行组合实现检测依赖文件中的依赖项安全。

利用Sonatype API实现依赖检测工具

利用Sonatype API实现依赖检测工具

public class Checker {    private static final String API_BASE_URL = "https://ossindex.sonatype.org/api/v3";    public  List<ScanResult> getAllVulns(String filePath) throws Exception {        List<ScanResult> scanResults=new ArrayList<ScanResult>();        try {            List<PomParser.Dependency> dependencies = PomParser.parse(filePath);            for (PomParser.Dependency dependency:dependencies){                scanResults.addAll(scanDependencies(dependency.getGroupId(),dependency.getArtifactId(),dependency.getVersion()));            }        }catch (Exception e){}        return scanResults;    }    /**     * 扫描指定 Maven 项目的所有依赖项,并返回包含 CVE 信息的扫描结果。     *     * @param groupId Maven 项目的 Group ID     * @param artifactId Maven 项目的 Artifact ID     * @param version Maven 项目的版本号     * @return 包含 CVE 信息的扫描结果列表     */    public List<ScanResult> scanDependencies(String groupId, String artifactId, String version) throws IOException, JSONException {        // 处理 CVE 数据并生成扫描结果        List<ScanResult> results = new ArrayList<>();        try{            // 构造 API 请求 URL            String url = API_BASE_URL + "/component-report/";            String content="{ "coordinates":["maven:"+groupId+"/"+artifactId+"@"+version+""]}";            // 解析 JSON 响应            String data = HttpsUtils.send(content, url);            int start = data.indexOf("[{");            data=data.substring(start+1,data.length()-1);            JSONObject jsonResponse = new JSONObject(data);            JSONArray vulnerabilities = jsonResponse.optJSONArray("vulnerabilities");            if (vulnerabilities != null) {                for (int i = 0; i < vulnerabilities.length(); i++) {                    JSONObject vuln = vulnerabilities.getJSONObject(i);                    String cveId = vuln.optString("cve");                    String description = vuln.optString("description");                    results.add(new ScanResult(cveId, description,groupId,artifactId,version));                }            }        }catch (Exception e){        }        return results;    }}// ScanResult结构体(自行生成Getter/Setter)public class ScanResult {    private String cveId;    private String description;    private String groupId;    private String artifactId;    private String version;    public ScanResult(String cveId, String description,String groupId,String artifactId,String version) {        this.cveId = cveId;        this.description = description;        this.groupId=groupId;        this.artifactId=artifactId;        this.version=version;    }}

需要注意的是这里有点坑,那就获取到的数据包含了响应头数据,故这里利用响应体结构特性进行了较为暴力的处理,当然也可以用其他方式实现,这里个人偷懒。

int start = data.indexOf("[{");data=data.substring(start+1,data.length()-1);

效果

以下为个人实现效果,这里没有给出我控制器这边代码,可自行琢磨封装使用。

利用Sonatype API实现依赖检测工具

结语

注意一下的是Sonatype并非只能检测Java依赖项安全,它支持多种语言的依赖检测,只不过这里以Java为例。

利用Sonatype API实现依赖检测工具

原文始发于微信公众号(安全之道):利用Sonatype API实现依赖检测工具

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月12日20:18:59
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   利用Sonatype API实现依赖检测工具https://cn-sec.com/archives/2496559.html

发表评论

匿名网友 填写信息