Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SHA - Unable to confirm integrity of downloaded file #41

Closed
sathomps opened this issue Jul 10, 2018 · 27 comments
Closed

SHA - Unable to confirm integrity of downloaded file #41

sathomps opened this issue Jul 10, 2018 · 27 comments

Comments

@sathomps
Copy link

I've updated my version of Jenkins to 2.131, using the juseppe-plugin and the update-sites-manager-plugin. When trying to install an internal plugin hosted and produced by the plugins above, I get the following error in Jenkins.

Attempt to verify a downloaded file (xxx.jpi.tmp) using SHA-512 failed since your configured update site does not provide this checksum. Falling back to weaker algorithms.
Jul 10, 2018 1:29:38 PM SEVERE hudson.model.UpdateCenter$DownloadJob run
Failed to install XXX
java.io.IOException: Unable to confirm integrity of downloaded file, refusing installation
@pcooke2002
Copy link

I am having this problem... is anyone else experiencing this? Do we have a fix? looks like they updated jenkins to require more stringent hash check

@sathomps
Copy link
Author

The fix is actually pretty simple, I haven't had a chance to submit a pull request.

plugin.json

{
    "type": "object",
    "properties": {
        "excerpt": {
            "type": "string"
        },
        "url": {
            "type": "string"
        },

        "releaseTimestamp": {
            "type": "string"
        },

        "buildDate": {
            "type": "string"
        },

        "version": {
            "type": "string"
        },

        "title": {
            "type": "string"
        },

        "wiki": {
            "type": "string"
        },

        "compatibleSinceVersion": {
            "type": "string"
        },

        "requiredCore": {
            "type": "string"
        },

        "builtBy": {
            "type": "string"
        },

        "name": {
            "type": "string"
        },

        "group": {
            "type": "string"
        },

        "sha1": {
            "type": "string"
        },

        "sha512": {
            "type": "string"
        },

        "dependencies": {
            "type": "array",
            "items": {
                "$ref": "dependency.json"
            }
        },

        "developers": {
            "type": "array",
            "items": {
                "$ref": "developer.json"
            }
        }

    }
}

HPI.java

/**
 * HPI Class for extracting HPI information from hpi file.
 *
 * @author JunHo Yoon
 */
public class HPI {
    private static final Logger LOG = LoggerFactory.getLogger(HPI.class);
    private static final Pattern DEVELOPERS_PATTERN = Pattern.compile("([^:]*):([^:]*):([^,]*),?");
    
    private static final String OPTIONAL_DEPENDENCY = ";resolution:=optional";
    
    public static final String PLUGIN_VERSION = "Plugin-Version";
    public static final String PLUGIN_WIKI_URL = "Url";
    public static final String PLUGIN_TITLE = "Long-Name";
    public static final String PLUGIN_NAME = "Short-Name";
    public static final String PLUGIN_COMPATIBLE_SINCE_VERSION = "Compatible-Since-Version";
    public static final String PLUGIN_REQUIRED_JENKINS_VERSION = "Hudson-Version";
    public static final String PLUGIN_BUILT_BY = "Built-By";
    public static final String PLUGIN_DEPENDENCIES = "Plugin-Dependencies";
    public static final String PLUGIN_DEVELOPERS = "Plugin-Developers";
    public static final String GROUP_ID = "Group-Id";
    
    public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
    
    public static Plugin loadHPI(final File file) throws IOException {
        final JarFile jarFile = new JarFile(file);
        final long timestamp = jarFile.getEntry(MANIFEST_PATH).getTime();
        
        final Plugin hpi = HPI.from(file, jarFile.getManifest().getMainAttributes(), timestamp);
        
        final String wiki = getWiki(file);
        if (StringUtils.isNotBlank(wiki)) {
            hpi.setWiki(wiki);
        }
        
        jarFile.close();
        
        return hpi.withExcerpt(getExcerpt(file));
    }
    
    private static Plugin from(final File file, final Attributes attributes, final long timestamp) throws IOException {
        final Digests digests = digests(file);
        
        return new Plugin()
                        .withReleaseTimestamp(releaseTimestampDateFormat().format(timestamp))
                        .withBuildDate(buildDateTimeFormat().format(timestamp))
                        .withName(
                                        notBlank(attributes.getValue(PLUGIN_NAME), "Plugin short name can't be empty"))
                        .withVersion(
                                        defaultIfBlank(
                                                        attributes.getValue(PLUGIN_VERSION),
                                                        attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION)))
                        .withGroup(attributes.getValue(GROUP_ID))
                        .withWiki(attributes.getValue(PLUGIN_WIKI_URL))
                        .withTitle(attributes.getValue(PLUGIN_TITLE))
                        .withCompatibleSinceVersion(attributes.getValue(PLUGIN_COMPATIBLE_SINCE_VERSION))
                        .withRequiredCore(attributes.getValue(PLUGIN_REQUIRED_JENKINS_VERSION))
                        .withBuiltBy(attributes.getValue(PLUGIN_BUILT_BY))
                        .withDependencies(getDependencies(attributes))
                        .withDevelopers(getDevelopers(attributes))
                        .withSha1(digests.sha1)
                        .withSha512(digests.sha512);
    }
    
    private static Digests digests(final File file) {
        final Digests digests = new Digests();
        try (FileInputStream fin = new FileInputStream(file)) {
            final MessageDigest sha1 = DigestUtils.getSha1Digest();
            final MessageDigest sha512 = DigestUtils.getSha512Digest();
            final byte[] buf = new byte[2048];
            int len;
            while ((len = fin.read(buf, 0, buf.length)) >= 0) {
                sha1.update(buf, 0, len);
                sha512.update(buf, 0, len);
            }
            
            digests.sha1 = new String(Base64.encodeBase64(sha1.digest()), "UTF-8");
            digests.sha512 = new String(Base64.encodeBase64(sha512.digest()), "UTF-8");
        } catch (final Exception ex) {
            LOG.error(ex.getMessage(), ex);
        }
        return digests;
    }
    
    private static SimpleDateFormat releaseTimestampDateFormat() {
        final SimpleDateFormat releaseFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'.00Z'", Locale.US);
        releaseFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        return releaseFormat;
    }
    
    private static SimpleDateFormat buildDateTimeFormat() {
        return new SimpleDateFormat("MMM dd, yyyy", Locale.US);
    }
    
    protected static List<Dependency> getDependencies(final Attributes attributes) throws IOException {
        final String deps = attributes.getValue(PLUGIN_DEPENDENCIES);
        if (deps == null) {
            return Collections.emptyList();
        }
        
        final List<Dependency> result = new ArrayList<>();
        for (final String token : deps.split(",")) {
            result.add(parseDependencyFrom(token));
        }
        return result;
    }
    
    protected static Dependency parseDependencyFrom(String token) {
        final boolean optional = token.endsWith(OPTIONAL_DEPENDENCY);
        if (optional) {
            token = substringBefore(token, OPTIONAL_DEPENDENCY);
        }
        
        final String[] pieces = token.split(":");
        
        return new Dependency()
                        .withName(pieces[0])
                        .withVersion(pieces[1])
                        .withOptional(optional);
    }
    
    protected static List<Developer> getDevelopers(final Attributes attributes) throws IOException {
        final String devs = attributes.getValue(PLUGIN_DEVELOPERS);
        if (isBlank(devs)) {
            return Collections.emptyList();
        }
        
        final List<Developer> result = new ArrayList<>();
        final Matcher matcher = DEVELOPERS_PATTERN.matcher(devs);
        int totalMatched = 0;
        while (matcher.find()) {
            result.add(new Developer()
                            .withName(trimToEmpty(matcher.group(1)))
                            .withDeveloperId(trimToEmpty(matcher.group(2)))
                            .withEmail(trimToEmpty(matcher.group(3))));
            totalMatched += matcher.end() - matcher.start();
        }
        if (totalMatched < devs.length()) {
            // ignore and move on
            LOG.error("Unparsable developer info: '{}'", devs.substring(totalMatched));
        }
        return result;
    }
    
    protected static String getWiki(final File hpiFile) {
        try {
            final String baseName = FilenameUtils.getBaseName(hpiFile.getName());
            final File exceptFile = new File(hpiFile.getParent(), baseName + ".wiki");
            if (exceptFile.exists()) {
                return FileUtils.readFileToString(exceptFile, "UTF-8");
            } else {
                return "";
            }
        } catch (final Exception e) {
            return "";
        }
    }
    
    protected static String getExcerpt(final File hpiFile) {
        try {
            final String baseName = FilenameUtils.getBaseName(hpiFile.getName());
            final File exceptFile = new File(hpiFile.getParent(), baseName + ".info");
            if (exceptFile.exists()) {
                return FileUtils.readFileToString(exceptFile, "UTF-8").replace("\r\n", "<br/>\n");
            } else {
                return "";
            }
        } catch (final Exception e) {
            return "";
        }
    }
    
    private static class Digests {
        private String sha1;
        private String sha512;
    }
    
}

@pcooke2002
Copy link

Hi thanks for posting this.
My employer prohibits me from modifying any OSS tools or contributing to any OSS source code.

@grazius
Copy link

grazius commented Sep 24, 2018

hi all
this fix need to be integrated or juseppe will stay unusable with last version of jenkins.

@sathomps
Copy link
Author

As it appears that this project is no longer being maintained, I'll create a fork this weekend with the update.

@lanwen
Copy link
Member

lanwen commented Sep 25, 2018

yes, sorry, have no time to maintain it anymore, as don't use it by myself for a long time

@lanwen
Copy link
Member

lanwen commented Sep 25, 2018

@sathomps actually you can send PR if you find a solution - will merge it

@sathomps
Copy link
Author

Thanks @lanwen for the update, understandable with the time commitments.

As I need this tool at work, I'll take over the project if you don't mind?

@lanwen
Copy link
Member

lanwen commented Sep 25, 2018

sure, I can give you write permissions

@sathomps
Copy link
Author

Thanks!

@lanwen
Copy link
Member

lanwen commented Sep 25, 2018

@sathomps done, but please always submit changes using PRs

@grazius
Copy link

grazius commented Sep 27, 2018

@sathomps hello,
now you can add this fix ?
you can generate the docker hub image also ?

@lanwen
Copy link
Member

lanwen commented Sep 27, 2018

docker hub image build automated from master and tags

@sathomps
Copy link
Author

@grazius I will be incorporating this change on Sunday afternoon.

@sathomps
Copy link
Author

sathomps commented Oct 2, 2018

Please reopen if you continue to have issues.

@sathomps sathomps closed this as completed Oct 2, 2018
@grazius
Copy link

grazius commented Oct 4, 2018

Nice thx
Can you also update the docker hub image with this fix?

@lanwen
Copy link
Member

lanwen commented Oct 4, 2018

@sathomps
Copy link
Author

sathomps commented Oct 6, 2018

Hmm - the tests on the merge said it was successful.

The logs referenced to the Docker build failed due to a timeout. How do you trigger a new docker build, not familiar with the process.

@lanwen
Copy link
Member

lanwen commented Oct 6, 2018

You can turn off tests on package phase in pom

@sathomps
Copy link
Author

sathomps commented Oct 6, 2018

Sure I know how to turn off tests in the pom but it's not really a solution :)

I'd like to trigger another travis build but it appears I don't have owner access to this repository - can't view settings.

@sathomps sathomps reopened this Oct 6, 2018
@lanwen
Copy link
Member

lanwen commented Oct 6, 2018

build is automated on docker hub side

@lanwen
Copy link
Member

lanwen commented Oct 6, 2018

since issue exists only while docker build, i suggest to turn the tests off while creating the container - as it adds no value after PR and master are tested

@sathomps
Copy link
Author

@pcooke2002
Copy link

Hi when will there be a release that includes this code? I see it checked in some time back and no new release???

@sathomps
Copy link
Author

Code is there and has been merged.

@caohhung
Copy link

caohhung commented Apr 17, 2019

Hi, latest release doesn't contain this fix https://github.com/jenkinsci/juseppe/releases

cc @lanwen

@vihoti
Copy link

vihoti commented Jan 24, 2022

Hi @sathomps
I am using v1.2.2 and for versions of Jenkins LTS >= 2.319.1 we are getting the same error message.
Should I open a new issue or does it suffice to mention it here?

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

No branches or pull requests

6 participants