Skip to content

Commit

Permalink
Refactor hibernate orm usage
Browse files Browse the repository at this point in the history
- VictimsHibernate is now used as a utility class
- VictimsDatabase now contains database interaction specifics
- [Issue victims#45] LastUpdate is now in-database
- Move to in database cache
- Update VictimsResultCacheTest to test in database caching
- Updated models
- Misc. code refactor
  • Loading branch information
abn committed Oct 26, 2014
1 parent 05ab2ae commit cd6e810
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 275 deletions.
24 changes: 12 additions & 12 deletions src/main/java/com/redhat/victims/database/VictimsDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@
/**
* A class providing easy instantiation of DB implementation based on the
* configured driver.
*
*
* @author abn
*
*
*/
public class VictimsDB {
/**
* The default driver class to use.
*
*
* @return
*/
public static String defaultDriver() {
Expand All @@ -47,7 +47,7 @@ public static String defaultDriver() {

/**
* Get the default url for a preconfigured driver.
*
*
* @return
*/
public static String defaultURL(String driver) {
Expand All @@ -63,7 +63,7 @@ public static String defaultURL(String driver) {

/**
* The default url for the default driver.
*
*
* @return
*/
public static String defaultURL() {
Expand All @@ -73,7 +73,7 @@ public static String defaultURL() {
/**
* Fetches an instance implementing {@link VictimsDBInterface} using the
* configured driver.
*
*
* @return A {@link VictimsDBInterface} implementation.
* @throws VictimsException
*/
Expand All @@ -88,14 +88,14 @@ public static VictimsDBInterface db() throws VictimsException {
+ VictimsConfig.Key.DB_URL);
}
}
return (VictimsDBInterface) new VictimsHibernate();
return (VictimsDBInterface) new VictimsDatabase();
}

/**
* This class facilitates use of multiple driver classes
*
*
* @author abn
*
*
*/
public static class Driver {
public static final String H2 = "org.h2.Driver";
Expand All @@ -108,7 +108,7 @@ public static class Driver {

/**
* Test if a given driver class is configured.
*
*
* @param driver
* The driver class.
* @return
Expand All @@ -119,7 +119,7 @@ public static boolean exists(String driver) {

/**
* Get the default connection URL for a given driver.
*
*
* @param driver
* The driver class.
* @param path
Expand All @@ -132,7 +132,7 @@ public static String url(String driver, String path) {

/**
* Add a driver to use.
*
*
* @param driver
* The driver class.
* @param urlFormat
Expand Down
280 changes: 280 additions & 0 deletions src/main/java/com/redhat/victims/database/VictimsDatabase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
package com.redhat.victims.database;

import com.redhat.victims.VictimsConfig;
import com.redhat.victims.VictimsException;
import com.redhat.victims.VictimsRecord;
import com.redhat.victims.VictimsService;
import com.redhat.victims.database.model.CVE;
import com.redhat.victims.database.model.FileHash;
import com.redhat.victims.database.model.Metadata;
import com.redhat.victims.database.model.Record;
import com.redhat.victims.database.model.Status;
import com.redhat.victims.fingerprint.Algorithms;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
* Created by abn on 10/26/14.
*/
public class VictimsDatabase implements VictimsDBInterface {

@Override
public Date lastUpdated() throws VictimsException {
Throwable throwable = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(
VictimsRecord.DATE_FORMAT);
Date since;

// The default start
since = sdf.parse("1970-01-01T00:00:00");

if (VictimsConfig.forcedUpdate()) {
return since;
}

Session session = VictimsHibernate.openSession();
Status lastUpdated = (Status) session.get(Status.class, StatusKey.LAST_UPDATED.toString());
if (lastUpdated == null) {
since = sdf.parse(lastUpdated.getValue());
}
return since;
} catch (ParseException e) {
throwable = e;
}
throw new VictimsException("Failed to retreive last updated data",
throwable);
}

private void setLastUpdate(Date date) {
Session session = VictimsHibernate.openSession();
try {
SimpleDateFormat sdf = new SimpleDateFormat(VictimsRecord.DATE_FORMAT);
session.save(new Status(StatusKey.LAST_UPDATED.toString(), sdf.format(date)));
} finally {
session.close();
}
}

protected void removeHashes(HashSet<String> hashes) {
Session session = VictimsHibernate.openSession();
String hql = "DELETE FROM Record r WHERE r.hash in :hashes";
try {
session.createQuery(hql).setParameterList("hashes", hashes).executeUpdate();
} finally {
session.close();
}
}

protected int remove(VictimsService.RecordStream recordStream) throws IOException {
HashSet<String> hashes = new HashSet<String>();
while (recordStream.hasNext()) {
hashes.add(recordStream.getNext().hash);
}
removeHashes(hashes);
return hashes.size();
}

protected int update(VictimsService.RecordStream recordStream) throws IOException {
int count = 0;
Session session = VictimsHibernate.openSession();
try {
Record record;
while (recordStream.hasNext()) {
VictimsRecord vr = recordStream.getNext();
String hash = vr.hash.trim();
Set<String> filehashes = vr.getHashes(Algorithms.SHA512).keySet();

record = new Record(hash, filehashes.size());
session.delete(record);
session.save(record);

Transaction transaction = session.beginTransaction();
Integer tcount = 0;

// insert filehashes
for (String filehash : filehashes) {
VictimsHibernate.saveBatch(session, tcount++, new FileHash(record, filehash));
}
// insert metadata key-value pairs
HashMap<String, String> md = vr.getFlattenedMetaData();
for (String key : md.keySet()) {
VictimsHibernate.saveBatch(session, tcount++, new Metadata(record, key.trim(), md.get(key).trim()));
}

// insert cves
for (String cve : vr.cves) {
VictimsHibernate.saveBatch(session, tcount++, new CVE(record, cve.trim()));
}

transaction.commit();

count++;
}
} finally {
session.close();
}
return count;
}

@Override
public void synchronize() throws VictimsException {

Throwable throwable = null;
try {
VictimsService service = new VictimsService();
Date since = lastUpdated();

int removed = remove(service.removed(since));
int updated = update(service.updates(since));

if (removed > 0 || updated > 0) {
VictimsResultCache.purge();
}

setLastUpdate(new Date());
} catch (IOException e) {
throwable = e;
}

if (throwable != null) {
throw new VictimsException("Failed to sync database", throwable);
}
}

/**
* Internal method implementing search for vulnerabilities checking if the
* given {@link VictimsRecord}'s contents are a superset of a record in the
* victims database.
*
* @param vr
* @return
*/
protected HashSet<String> getEmbeddedVulnerabilities(VictimsRecord vr) {
HashSet<String> cves = new HashSet<String>();

Set<String> fileHashes = vr.getHashes(Algorithms.SHA512).keySet();
if (fileHashes.size() <= 0) {
return cves;
}

Session session = VictimsHibernate.openSession();
try {
Criteria matchCriteria = session.createCriteria(FileHash.class)
.add(Restrictions.in("filehash", fileHashes))
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("record"))
.add(Projections.count("filehash")));
for (Object object : matchCriteria.list()) {
Object[] tuple = (Object[]) object;
Record record = (Record) tuple[0];
Long count = (Long) tuple[1];
if (count.equals(new Long(record.getFileCount()))) {
for (CVE c : record.getCves()) {
cves.add(c.getCve());
}
}
}
} finally {
session.close();
}
return cves;
}

public HashSet<String> getVulnerabilities(VictimsRecord vr)
throws VictimsException {
try {

HashSet<String> cached = VictimsResultCache.get(vr.hash);
if (cached != null) {
return cached;
}

HashSet<String> cves = new HashSet<String>();

// Match jar sha512
cves.addAll(getVulnerabilities(vr.hash.trim()));

// Match any embedded filehashes
cves.addAll(getEmbeddedVulnerabilities(vr));

VictimsResultCache.put(vr.hash, cves);
return cves;
} catch (Throwable e) {
throw new VictimsException(
"Could not determine vulnerabilities for hash: " + vr.hash,
e);
}
}

@Override
public HashSet<String> getVulnerabilities(String sha512) throws VictimsException {
Session session = VictimsHibernate.openSession();
try {
HashSet<String> cves = new HashSet<String>();
for (Record record : (List<Record>) session.createCriteria(Record.class)
.add(Restrictions.eq("hash", sha512)).list()) {
cves.addAll(record.getCveNames());
}
return cves;
} finally {
session.close();
}
}

@Override
public HashSet<String> getVulnerabilities(HashMap<String, String> props) throws VictimsException {
Session session = VictimsHibernate.openSession();
try {
HashSet<String> cves = new HashSet<String>();

List<Metadata.MetadataProperty> properties = new ArrayList<Metadata.MetadataProperty>();
for (String key : props.keySet()) {
properties.add(new Metadata.MetadataProperty(key, props.get(key)));
}

String hql = "SELECT m.record, count(*) FROM Metadata m WHERE m.property IN :propset GROUP BY m.record";
Query propMatch = session.createQuery(hql);
propMatch.setParameterList("propset", properties);

for (Object obj : propMatch.list()) {
Object[] tuple = (Object[]) obj;
Record record = (Record) tuple[0];
Long count = (Long) tuple[1];
if (count.equals(new Long(props.size()))) {
for (CVE c : record.getCves()) {
cves.add(c.getCve());
}
}
}
return cves;
} finally {
session.close();
}
}

@Override
public int getRecordCount() throws VictimsException {
Session session = VictimsHibernate.openSession();
try {
return (Integer) session.createCriteria(Record.class).setProjection(Projections.rowCount()).list().get(0);
} finally {
session.close();
}
}

}
Loading

0 comments on commit cd6e810

Please sign in to comment.