rupert 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +42 -0
- data/Rakefile +34 -0
- data/TODO.md +39 -0
- data/lib/rupert/errors.rb +3 -0
- data/lib/rupert/parser.rb +60 -0
- data/lib/rupert/rpm/lead.rb +143 -0
- data/lib/rupert/rpm/signature/entry.rb +19 -0
- data/lib/rupert/rpm/signature/index.rb +32 -0
- data/lib/rupert/rpm/signature/store.rb +35 -0
- data/lib/rupert/rpm/signature.rb +43 -0
- data/lib/rupert/rpm.rb +118 -0
- data/lib/rupert/version.rb +3 -0
- data/lib/rupert.rb +2 -0
- data/rupert.gemspec +34 -0
- data/test/end_to_end/rpm_signature_test.rb +14 -0
- data/test/end_to_end/rpm_test.rb +50 -0
- data/test/fixtures/README.md +6 -0
- data/test/fixtures/notanrpm-0.0.1-1.el6.noarch.rpm +0 -0
- data/test/fixtures/redhat-lsb-4.0-7.el6.centos.src.rpm +0 -0
- data/test/fixtures/rpm-4.8.0-32.el6.x86_64.rpm +0 -0
- data/test/fixtures/rpm-libs-4.8.0-32.el6.i686.rpm +0 -0
- data/test/fixtures/rpm-libs-4.8.0-32.el6.x86_64.rpm +0 -0
- data/test/fixtures/rpmdevtools-7.5-2.el6.noarch.rpm +0 -0
- data/test/test_helper.rb +65 -0
- data/test/unit/rpm/lead_test.rb +100 -0
- data/test/unit/rpm/signature/index_test.rb +14 -0
- data/test/unit/rpm/signature/store_test.rb +12 -0
- data/test/unit/rpm/signature_test.rb +25 -0
- data/test/unit/rpm_test.rb +19 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 41a2074b3a0e08e088990219f5659323705498e3
|
4
|
+
data.tar.gz: 810e02415872bb758efa3b109f70e5d264f8455f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3b351ae5740f728a267a41afc8a474921aaf072d3141209cc86d743e4011ebe223adb5c6fb45db7c3bb29d4b67d5cbae99d6748c564264f97b2160508db7f626
|
7
|
+
data.tar.gz: 1585d64649df3e91617ddb3b4b88464d8a4f3aac419e18c862ee1009de1915554502be5c793257d79d4a0f39164008d98170bb79bbe59787f3a9ff75e2eba24f
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Stefano Zanella
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
[](https://travis-ci.org/stefanozanella/rupert)
|
2
|
+
[](https://codeclimate.com/github/stefanozanella/rupert)
|
3
|
+
[](https://coveralls.io/r/stefanozanella/rupert?branch=master)
|
4
|
+
[](https://gemnasium.com/stefanozanella/rupert)
|
5
|
+
|
6
|
+
# Rupert
|
7
|
+
|
8
|
+
Pure Ruby RPM Library
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'rupert'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install rupert
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
You can read an RPM simply with:
|
27
|
+
|
28
|
+
rpm = Rupert::RPM.load('rpm-4.8.0-32.el6.x86_64.rpm')
|
29
|
+
|
30
|
+
or just check if a specific file is an RPM with:
|
31
|
+
|
32
|
+
Rupert::RPM.rpm? 'iamtrollingyou' # false
|
33
|
+
|
34
|
+
(note that loading a file that is not an RPM generates an exception)
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
1. Fork it
|
39
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
40
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
41
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
42
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'coveralls/rake/task'
|
3
|
+
|
4
|
+
namespace :gem do
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
end
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.verbose = true
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :test do
|
16
|
+
%w{end_to_end unit}.each do |suite|
|
17
|
+
Rake::TestTask.new(suite.to_sym) do |t|
|
18
|
+
t.verbose = true
|
19
|
+
t.libs << 'lib'
|
20
|
+
t.libs << 'test'
|
21
|
+
t.test_files = FileList["test/#{suite}/**/*_test.rb"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Analyze code duplication"
|
27
|
+
task :flay do
|
28
|
+
system "flay lib/*.rb"
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Analyze code complexity"
|
32
|
+
task :flog do
|
33
|
+
system "find lib -name \*.rb | xargs flog"
|
34
|
+
end
|
data/TODO.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# TODO (Features, Tests et al.)
|
2
|
+
|
3
|
+
* If a file is not an RPM, every attempt to read anything should misreably
|
4
|
+
fail.
|
5
|
+
* Add control logic to verify if a file exists before creating a new RPM in
|
6
|
+
Rupture::RPM#load
|
7
|
+
* describe RPM::Lead it fails nicely when architecture is unknown -> def arch
|
8
|
+
return @@arch_map[@archnum] || "unknown"
|
9
|
+
* Recognize all architectures
|
10
|
+
* Edge case: verify correct idenfitication of `noarch`
|
11
|
+
* Recognize all the OSes (maybe also add `osnum` method?)
|
12
|
+
* How to fail when os is not recognized?
|
13
|
+
* Document references to RPM spec docs and source code
|
14
|
+
* Document Rupture::RPM::Lead (summarize structure and purpose from rpm file
|
15
|
+
format document)
|
16
|
+
* Return nil or "" as md5 if no signature found (this means not creating the
|
17
|
+
signature if lead says no). Consider also this: if a signature is found and
|
18
|
+
md5 is not present, then mandatory rules are not violated. Finally, I'm not
|
19
|
+
sure it's so optional to include a signature; I suspect it's mandatory
|
20
|
+
(perhaps because md5 is mandatory?).
|
21
|
+
* Document Rupture::RPM::Signature::Index::Entry class (in particular, how the
|
22
|
+
meaning of the `count` field changes with data type
|
23
|
+
* Store#get error handling. Pass nil, out of bound address, non-numerical
|
24
|
+
address
|
25
|
+
* Use actual size contained in the signature to calculate checksum?
|
26
|
+
* Remember that signature is not mandatory in RPM!
|
27
|
+
* It looks like size is also a form of signature, in the sense that RPM uses
|
28
|
+
the stored value to check actual size of header + payload (or did I
|
29
|
+
understood wrong?).
|
30
|
+
* I18N ???
|
31
|
+
|
32
|
+
# Roadmap
|
33
|
+
|
34
|
+
* Pick the entry type table. For each type, pick a meaningful header tag (like
|
35
|
+
name, file list, etc.) to derive a feature to be implemented. At the end, all
|
36
|
+
types should be correctly handled
|
37
|
+
* Decide a sensible behaviour for missing mandatory tags. Then, pick all
|
38
|
+
mandatory header tags and build a feature for them if not already covered
|
39
|
+
with previous method.
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rupert/rpm/lead'
|
2
|
+
require 'rupert/rpm/signature'
|
3
|
+
|
4
|
+
module Rupert
|
5
|
+
class Parser
|
6
|
+
def initialize(raw_io)
|
7
|
+
@raw_io = raw_io
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse
|
11
|
+
# TODO Fit to current design (i.e. no parsing in Lead c'tor?)
|
12
|
+
lead = RPM::Lead.new(@raw_io)
|
13
|
+
|
14
|
+
entry_count, store_size = parse_header
|
15
|
+
entries = parse_entries(entry_count)
|
16
|
+
|
17
|
+
store = parse_store(store_size)
|
18
|
+
content = parse_content
|
19
|
+
|
20
|
+
signature = RPM::Signature.new(RPM::Signature::Index.new(entries, store))
|
21
|
+
|
22
|
+
RPM.new(lead, signature, content)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def parse_header
|
28
|
+
header_size = 16
|
29
|
+
header_format = "@8NN"
|
30
|
+
|
31
|
+
@raw_io.read(header_size).unpack(header_format)
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_entries(count)
|
35
|
+
entry_size = 16
|
36
|
+
entry_format = "NNNN"
|
37
|
+
|
38
|
+
entries = Hash.new
|
39
|
+
count.times do
|
40
|
+
tag, type, offset, count = @raw_io.read(entry_size).unpack(entry_format)
|
41
|
+
entry = RPM::Signature::Entry.new tag, type, offset, count
|
42
|
+
entries[entry.tag] = entry
|
43
|
+
end
|
44
|
+
|
45
|
+
entries
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_store(size)
|
49
|
+
RPM::Signature::Store.new(StringIO.new(@raw_io.read(nearest_multiple(size, 8))))
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_content
|
53
|
+
@raw_io.read
|
54
|
+
end
|
55
|
+
|
56
|
+
def nearest_multiple(size, modulo)
|
57
|
+
(size / modulo.to_f).ceil * modulo
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Rupert
|
2
|
+
class RPM
|
3
|
+
class Lead
|
4
|
+
# Lead has a fixed length
|
5
|
+
LEAD_LENGTH = 96.freeze # byte
|
6
|
+
|
7
|
+
# The magic header that identifies an RPM beyond a shadow of a doubt, as
|
8
|
+
# every good RPM starts with hex `ed ab ee db`.
|
9
|
+
MAGIC = "\xed\xab\xee\xdb".force_encoding(Encoding::ASCII_8BIT).freeze
|
10
|
+
|
11
|
+
# RPM of type binary
|
12
|
+
TYPE_BINARY = 0.freeze
|
13
|
+
|
14
|
+
# RPM of type source
|
15
|
+
TYPE_SOURCE = 1.freeze
|
16
|
+
|
17
|
+
@@arch_map = {
|
18
|
+
1 => "i386/x86_64"
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
@@os_map = {
|
22
|
+
1 => "Linux"
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
# Only valid and recognized signature type
|
26
|
+
SIGNATURE_TYPE_HEADER = 5.freeze
|
27
|
+
|
28
|
+
# Chomps given IO, producing a `Lead` object and returning the remaining
|
29
|
+
# part for subsequent processing.
|
30
|
+
#
|
31
|
+
# Lead data is expected to begin at IO start, so returned scrap is
|
32
|
+
# basically the input IO without its first
|
33
|
+
# `Rupert::RPM::Lead::LEAD_LENGTH` bytes.
|
34
|
+
#
|
35
|
+
# @param io [IO] IO object containing lead data at its start, possibly
|
36
|
+
# with additional bytes at the end
|
37
|
+
#
|
38
|
+
# @return [Rupert::RPM::Lead, IO] the lead object corresponding to the
|
39
|
+
# data at the beginning of the IO, and the part of the input remaining
|
40
|
+
# after parsing.
|
41
|
+
def self.chomp(io)
|
42
|
+
[ self.new(io), io ]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Initializes a lead section, parsing given IO
|
46
|
+
#
|
47
|
+
# @param lead [IO] An IO containing the lead information at the start
|
48
|
+
def initialize(lead)
|
49
|
+
lead_data = lead.read(LEAD_LENGTH)
|
50
|
+
parse(lead_data) if lead_data
|
51
|
+
end
|
52
|
+
|
53
|
+
# Major number of the RPM version used to format the package
|
54
|
+
#
|
55
|
+
# @return [Fixnum] RPM major version number
|
56
|
+
def rpm_version_major
|
57
|
+
@rpm_major
|
58
|
+
end
|
59
|
+
|
60
|
+
# Minor number of the RPM version used to format the package
|
61
|
+
#
|
62
|
+
# @return [Fixnum] RPM minor version number
|
63
|
+
def rpm_version_minor
|
64
|
+
@rpm_minor
|
65
|
+
end
|
66
|
+
|
67
|
+
# RPM version used to format the package
|
68
|
+
#
|
69
|
+
# @return [String] RPM version in <major>.<minor> format
|
70
|
+
def rpm_version
|
71
|
+
"#{rpm_version_major}.#{rpm_version_minor}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Tells if the file is recognized as an RPM or not
|
75
|
+
#
|
76
|
+
# @return `true` if magic number is found at lead's start, `false`
|
77
|
+
# otherwise
|
78
|
+
def rpm?
|
79
|
+
@magic == MAGIC
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return `true` if lead reports RPM as of binary type
|
83
|
+
def binary_type?
|
84
|
+
@type == TYPE_BINARY
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return `true` if lead reports RPM as of source type
|
88
|
+
def source_type?
|
89
|
+
@type == TYPE_SOURCE
|
90
|
+
end
|
91
|
+
|
92
|
+
# The architecture the package was built for. A list of supported
|
93
|
+
# architectures can be found in `/usr/lib/rpm/rpmrc` on RedHat based
|
94
|
+
# systems.
|
95
|
+
#
|
96
|
+
# @return [String] a string representing the architecture name(s)
|
97
|
+
def arch
|
98
|
+
@@arch_map[@archnum]
|
99
|
+
end
|
100
|
+
|
101
|
+
# The name of the package
|
102
|
+
#
|
103
|
+
# @return [String] package name in the form <name>-<version>-<rev>.<suffix>
|
104
|
+
def name
|
105
|
+
@name
|
106
|
+
end
|
107
|
+
|
108
|
+
# OS for which the package was built
|
109
|
+
#
|
110
|
+
# @return [String] OS canonical name as defined in `/usr/lib/rpm/rpmrc`
|
111
|
+
def os
|
112
|
+
@@os_map[@osnum]
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return `true` if the RPM is recognized as being signed, `false` otherwise
|
116
|
+
def signed?
|
117
|
+
@signature_type == SIGNATURE_TYPE_HEADER
|
118
|
+
end
|
119
|
+
|
120
|
+
# String of reserved bits. It's here for completeness of the lead's
|
121
|
+
# implementation but it isn't intended to be actually used
|
122
|
+
#
|
123
|
+
# @return [String] the raw 16 bytes long reserved string at the end of
|
124
|
+
# the lead
|
125
|
+
def reserved
|
126
|
+
@reserved
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# :nodoc
|
132
|
+
# The format string passed to `unpack` to parse the lead
|
133
|
+
LEAD_FORMAT = "A4CCnnZ66nna16".freeze
|
134
|
+
|
135
|
+
# :nodoc
|
136
|
+
# Unpacks lead raw bytes into its semantic components
|
137
|
+
def parse(lead_data)
|
138
|
+
@magic, @rpm_major, @rpm_minor, @type, @archnum, @name, @osnum,
|
139
|
+
@signature_type, @reserved = lead_data.unpack(LEAD_FORMAT)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rupert
|
2
|
+
class RPM
|
3
|
+
class Signature
|
4
|
+
class Entry
|
5
|
+
# Initializes a new index entry.
|
6
|
+
#
|
7
|
+
# @param tag [String] 4 byte entry tag (semantic data type)
|
8
|
+
# @param type [String] 4 byte entry data format
|
9
|
+
# @param offset [String] 4 byte pointer to data in the index store
|
10
|
+
# @param count [String] 4 byte number of data items held by the entry
|
11
|
+
def initialize(tag, type, offset, count)
|
12
|
+
@tag, @type, @offset, @count = tag, type, offset, count
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :tag, :type, :offset, :count
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rupert/rpm/signature/entry'
|
2
|
+
require 'rupert/rpm/signature/store'
|
3
|
+
|
4
|
+
module Rupert
|
5
|
+
class RPM
|
6
|
+
class Signature
|
7
|
+
class Index
|
8
|
+
# Initializes a new signature index, given the header's entries and the
|
9
|
+
# store containing actual data.
|
10
|
+
#
|
11
|
+
# @param entries [Hash] a map of
|
12
|
+
# Rupert::RPM::Signature::Entry, indexed by tag
|
13
|
+
# @param store [Rupert::RPM::Signature::Store] store containing
|
14
|
+
# data pointed by entries
|
15
|
+
def initialize(entries, store)
|
16
|
+
@entries = entries
|
17
|
+
@store = store
|
18
|
+
end
|
19
|
+
|
20
|
+
# Retrieves data pointed by given tag.
|
21
|
+
#
|
22
|
+
# @param tag [Fixnum] data type
|
23
|
+
# @return [Object] data associated to given tag, with variable format
|
24
|
+
# depending on how it's stored
|
25
|
+
def get(tag)
|
26
|
+
entry = @entries[tag]
|
27
|
+
@store.fetch(entry)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Rupert
|
2
|
+
class RPM
|
3
|
+
class Signature
|
4
|
+
# Package information is mostly contained in headers. Headers are composed
|
5
|
+
# of an index and a store.
|
6
|
+
# The (raw) store holds semantic RPM information in an undefined
|
7
|
+
# order and without any structure (i.e. by concatenating all pieces
|
8
|
+
# together). Addressing of resources in the store is handled by header
|
9
|
+
# entries, which define data format, position and size. Responsibility
|
10
|
+
# of the store is to take care of extracting these pieces given proper
|
11
|
+
# addressing information.
|
12
|
+
class Store
|
13
|
+
# Creates a new store by wrapping given chunck of raw data into a Store
|
14
|
+
# object.
|
15
|
+
#
|
16
|
+
# @param io [IO] raw binary data carved from RPM header. Must be an IO
|
17
|
+
# containing only store's data (i.e. entry addresses are considered
|
18
|
+
# relative to 0)
|
19
|
+
def initialize(io)
|
20
|
+
@io = io
|
21
|
+
end
|
22
|
+
|
23
|
+
# Fetches data pointed by given entry.
|
24
|
+
#
|
25
|
+
# @param entry [Rupert::RPM::Signature::Entry] entry containing address
|
26
|
+
# and type of needed information
|
27
|
+
# @return [String] binary string containing asked data
|
28
|
+
def fetch(entry)
|
29
|
+
@io.seek(entry.offset, IO::SEEK_SET)
|
30
|
+
@io.read(entry.count)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rupert/rpm/signature/index'
|
2
|
+
|
3
|
+
module Rupert
|
4
|
+
class RPM
|
5
|
+
class Signature
|
6
|
+
# Tag holding 128-bit MD5 checksum of header and payload
|
7
|
+
MD5_TAG = 1004.freeze
|
8
|
+
|
9
|
+
# Creates a new signature given its components.
|
10
|
+
#
|
11
|
+
# @param index [Rupert::RPM::Signature::Index] the signature index
|
12
|
+
# containing actual signature data
|
13
|
+
def initialize(index)
|
14
|
+
@index = index
|
15
|
+
end
|
16
|
+
|
17
|
+
# MD5 checksum contained in the RPM.
|
18
|
+
#
|
19
|
+
# @return [String] 128-bit MD5 checksum of RPM's header and payload, in
|
20
|
+
# raw binary form.
|
21
|
+
def md5
|
22
|
+
@index.get MD5_TAG
|
23
|
+
end
|
24
|
+
|
25
|
+
# Verifies if stored MD5 checksum corresponds to digest calculated over
|
26
|
+
# given content.
|
27
|
+
#
|
28
|
+
# @return `true` if stored MD5 checksum corresponds to MD5 calculated
|
29
|
+
# over given content, `false` otherwise
|
30
|
+
def verify_checksum(content)
|
31
|
+
md5 == md5_checksum(content)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# :nodoc
|
37
|
+
# MD5 checksum of given string
|
38
|
+
def md5_checksum(str)
|
39
|
+
Digest::MD5.digest(str)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/rupert/rpm.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'rupert/errors'
|
2
|
+
require 'rupert/parser'
|
3
|
+
require 'rupert/rpm/lead'
|
4
|
+
require 'rupert/rpm/signature'
|
5
|
+
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
module Rupert
|
9
|
+
class RPM
|
10
|
+
class << self
|
11
|
+
# Loads a RPM file and parses its structure
|
12
|
+
#
|
13
|
+
# @param filename [String] filename of the RPM to load
|
14
|
+
# @return [Rupert::RPM] the parsed RPM
|
15
|
+
def load(filename)
|
16
|
+
raise NotAnRPM,
|
17
|
+
"File #{filename} isn't a valid RPM" unless rpm?(filename)
|
18
|
+
|
19
|
+
raw_io = File.open(filename, 'r')
|
20
|
+
rpm = Parser.new(raw_io).parse
|
21
|
+
raw_io.close
|
22
|
+
|
23
|
+
return rpm
|
24
|
+
end
|
25
|
+
|
26
|
+
# Tells whether given filename points to a valid RPM or not.
|
27
|
+
#
|
28
|
+
# @param filename [String] filename to inspect
|
29
|
+
# @return `true` if file starts with the correct magic header
|
30
|
+
def rpm?(filename)
|
31
|
+
Lead.new(File.open(filename, 'r')).rpm?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Initialize the RPM object, given its components.
|
36
|
+
#
|
37
|
+
# This method is not intended to be used to instantiate RPM objects
|
38
|
+
# directly. Instead, use Rupert::RPM::load for a more straightforward
|
39
|
+
# alternative.
|
40
|
+
#
|
41
|
+
# @param lead [Rupert::RPM::Lead] RPM lead section
|
42
|
+
# @param signature [Rupert::RPM::Signature] RPM signature section
|
43
|
+
# @param content [String] Raw content found after the signature structure
|
44
|
+
def initialize(lead, signature, content)
|
45
|
+
@lead = lead
|
46
|
+
@signature = signature
|
47
|
+
@content = content
|
48
|
+
end
|
49
|
+
|
50
|
+
# RPM version used to encode the package.
|
51
|
+
#
|
52
|
+
# @return [String] the RPM version in `<major>.<minor>` format
|
53
|
+
def rpm_version
|
54
|
+
@lead.rpm_version
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return `true` if the RPM is of type binary, `false` otherwise
|
58
|
+
def binary?
|
59
|
+
@lead.binary_type?
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return `true` if the RPM is of type source, `false` otherwise
|
63
|
+
def source?
|
64
|
+
@lead.source_type?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Which architecture the package was built for, e.g. `i386/x86_64` or
|
68
|
+
# `arm`
|
69
|
+
#
|
70
|
+
# @return [String] package architecture name
|
71
|
+
def rpm_arch
|
72
|
+
@lead.arch
|
73
|
+
end
|
74
|
+
|
75
|
+
# Full package name
|
76
|
+
#
|
77
|
+
# @return [String] package name in the form <name>-<version>-<rev>.<suffix>
|
78
|
+
def name
|
79
|
+
@lead.name
|
80
|
+
end
|
81
|
+
|
82
|
+
# OS for which the package was built
|
83
|
+
#
|
84
|
+
# @return [String] as defined in /usr/lib/rpm/rpmrc under the canonical OS
|
85
|
+
# names section
|
86
|
+
def os
|
87
|
+
@lead.os
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return `true` if the package is signed, `false` otherwise
|
91
|
+
def signed?
|
92
|
+
@lead.signed?
|
93
|
+
end
|
94
|
+
|
95
|
+
# MD5 checksum stored in the package (base64 encoded). To be used to check
|
96
|
+
# package integrity.
|
97
|
+
#
|
98
|
+
# NOTE: This is not the MD5 of the whole package; rather, the digest is
|
99
|
+
# calculated over the header and payload, leaving out the lead and the
|
100
|
+
# signature header. I.e., running `md5sum <myrpm>` won't held the same
|
101
|
+
# result as `Rupert::RPM.load('<myrpm>').md5`.
|
102
|
+
#
|
103
|
+
# @return [String] Base64-encoded MD5 checksum of package's header and
|
104
|
+
# payload, stored in the RPM itself
|
105
|
+
def md5
|
106
|
+
Base64.strict_encode64(@signature.md5)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Verifies package integrity. Compares MD5 checksum stored in the package
|
110
|
+
# with checksum calculated over header(s) and payload (archive).
|
111
|
+
#
|
112
|
+
# @return `true` if package is intact, `false` if package (either stored MD5 or
|
113
|
+
# payload) is corrupted
|
114
|
+
def intact?
|
115
|
+
@signature.verify_checksum(@content)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/rupert.rb
ADDED
data/rupert.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.unshift('lib') unless $:.include?('lib')
|
3
|
+
require 'rupert/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "rupert"
|
7
|
+
spec.version = Rupert::VERSION
|
8
|
+
spec.authors = ["Stefano Zanella"]
|
9
|
+
spec.email = ["zanella.stefano@gmail.com"]
|
10
|
+
spec.summary = %q{Pure Ruby RPM Library}
|
11
|
+
spec.homepage = ""
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.extra_rdoc_files = ["README.md"]
|
20
|
+
spec.rdoc_options = ["--charset=UTF-8"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5"
|
25
|
+
spec.add_development_dependency "minitest-spec-context"
|
26
|
+
spec.add_development_dependency "mocha", "~> 0"
|
27
|
+
spec.add_development_dependency "coveralls"
|
28
|
+
spec.add_development_dependency "flog"
|
29
|
+
spec.add_development_dependency "flay"
|
30
|
+
|
31
|
+
spec.description = %s{
|
32
|
+
Rupert allows to manipulate RPM files independently from availability of rpmlib.
|
33
|
+
}
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Rupert::RPM do
|
4
|
+
let(:valid_rpm) { fixture('rpm-4.8.0-32.el6.x86_64.rpm') }
|
5
|
+
let(:rpm) { Rupert::RPM.load(valid_rpm) }
|
6
|
+
|
7
|
+
it "reads the package checksum" do
|
8
|
+
rpm.md5.must_equal "jvA7YQW9TICIGWjaSLF/sA=="
|
9
|
+
end
|
10
|
+
|
11
|
+
it "verifies package (non-cryptographic) integrity" do
|
12
|
+
assert rpm.intact?, "expected package to be intact, but it was not."
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Rupert::RPM do
|
4
|
+
let(:valid_rpm) { fixture('rpm-4.8.0-32.el6.x86_64.rpm') }
|
5
|
+
let(:invalid_rpm) { fixture('notanrpm-0.0.1-1.el6.noarch.rpm') }
|
6
|
+
let(:bin_rpm) { fixture('rpm-4.8.0-32.el6.x86_64.rpm') }
|
7
|
+
let(:src_rpm) { fixture('redhat-lsb-4.0-7.el6.centos.src.rpm') }
|
8
|
+
|
9
|
+
let(:rpm) { Rupert::RPM.load(valid_rpm) }
|
10
|
+
let(:binary_rpm) { Rupert::RPM.load(bin_rpm) }
|
11
|
+
let(:source_rpm) { Rupert::RPM.load(src_rpm) }
|
12
|
+
|
13
|
+
it "correctly loads a valid RPM" do
|
14
|
+
Rupert::RPM.load(valid_rpm)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "raises an error if the RPM is not in valid format" do
|
18
|
+
proc {
|
19
|
+
Rupert::RPM.load(invalid_rpm)
|
20
|
+
}.must_raise Rupert::NotAnRPM
|
21
|
+
end
|
22
|
+
|
23
|
+
it "knows which version of RPM the file is" do
|
24
|
+
rpm.rpm_version.must_equal "3.0"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "tells which architecture the package is built for" do
|
28
|
+
rpm.rpm_arch.must_equal "i386/x86_64"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "tells if the file is a binary or source RPM" do
|
32
|
+
assert binary_rpm.binary?, "failed to recognize RPM as binary"
|
33
|
+
refute binary_rpm.source?, "RPM misrecognized as of source type"
|
34
|
+
|
35
|
+
assert source_rpm.source?, "failed to recognize RPM as source"
|
36
|
+
refute source_rpm.binary?, "RPM misrecognized as of binary type"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "tells the package's name" do
|
40
|
+
rpm.name.must_equal "rpm-4.8.0-32.el6"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "tells the operating system for which the package has been built" do
|
44
|
+
rpm.os.must_equal "Linux"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "tells if package is signed" do
|
48
|
+
assert rpm.signed?, "expected the package to be signed, but it was not"
|
49
|
+
end
|
50
|
+
end
|
File without changes
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'minitest/pride'
|
6
|
+
require 'minitest/spec'
|
7
|
+
require 'minitest-spec-context'
|
8
|
+
require 'mocha/setup'
|
9
|
+
|
10
|
+
require 'rupert'
|
11
|
+
require 'rupert/errors'
|
12
|
+
|
13
|
+
# Returns the absolute path for the given fixture filename, or the path of
|
14
|
+
# fixture directory if no argument present.
|
15
|
+
def fixture(filename='')
|
16
|
+
File.join(File.dirname(__FILE__), 'fixtures', filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Forces the string to be encoded as 8 bit ASCII (instead of, for example,
|
20
|
+
# UTF-8). This is to be used whenever a (binary) string is manually hardcoded
|
21
|
+
# in tests, since using a different encoding (e.g. UTF-8) breaks
|
22
|
+
# parsing/generating the IO stream.
|
23
|
+
def ascii(string)
|
24
|
+
string.force_encoding(Encoding::ASCII_8BIT)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Pads a string with nulls to fill given length
|
28
|
+
def pad(string, length)
|
29
|
+
("%-#{length}.#{length}s" % string).gsub(" ", "\x00")
|
30
|
+
end
|
31
|
+
|
32
|
+
# A string of nulls of given length
|
33
|
+
def null(length)
|
34
|
+
pad("", length)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns 128-bit MD5 checksum of given string
|
38
|
+
def md5(str)
|
39
|
+
Digest::MD5.digest(str)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Transforms given string into an IO object.
|
43
|
+
def io(string)
|
44
|
+
StringIO.new(string)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Helpers that builds a lead header from a plain string. Use it to make tests
|
48
|
+
# more readable.
|
49
|
+
def lead_from(string)
|
50
|
+
Rupert::RPM::Lead.new(io(string))
|
51
|
+
end
|
52
|
+
|
53
|
+
def lead_with(string)
|
54
|
+
lead_from(string)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Helpers that builds a signature header from a plain string. Use it to make tests
|
58
|
+
# more readable.
|
59
|
+
def signature_from(string)
|
60
|
+
Rupert::RPM::Signature.new(io(string))
|
61
|
+
end
|
62
|
+
|
63
|
+
def signature_with(string)
|
64
|
+
signature_from(string)
|
65
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Rupert::RPM::Lead do
|
4
|
+
context "when reading a proper RPM" do
|
5
|
+
let(:rpm_version_raw) { ascii("\x02\x01") }
|
6
|
+
let(:binary_type_raw) { ascii("\x00\x00") }
|
7
|
+
let(:source_type_raw) { ascii("\x00\x01") }
|
8
|
+
let(:archnum_raw) { ascii("\x00\x01") }
|
9
|
+
let(:pkg_name_raw) { ascii(pad("awesomepkg-1.0-127.fedora19", 66)) }
|
10
|
+
let(:os_raw) { ascii("\x00\x01") }
|
11
|
+
let(:sig_type_header_raw) { ascii("\x00\x05") }
|
12
|
+
let(:unknown_sig_type_raw) { ascii("\x03\x03") }
|
13
|
+
let(:reserved_string_raw) { ascii(null(16)) }
|
14
|
+
|
15
|
+
let(:rpm_version) { "#{Rupert::RPM::Lead::MAGIC}#{rpm_version_raw}" }
|
16
|
+
let(:binary_type) { "#{rpm_version}#{binary_type_raw}" }
|
17
|
+
let(:source_type) { "#{rpm_version}#{source_type_raw}" }
|
18
|
+
let(:arch) { "#{binary_type}#{archnum_raw}" }
|
19
|
+
let(:pkg_name) { "#{arch}#{pkg_name_raw}" }
|
20
|
+
let(:os) { "#{pkg_name}#{os_raw}" }
|
21
|
+
let(:signature_type_header) { "#{os}#{sig_type_header_raw}" }
|
22
|
+
let(:unknown_signature_type) { "#{os}#{unknown_sig_type_raw}" }
|
23
|
+
let(:reserved_bits) { "#{signature_type_header}#{reserved_string_raw}" }
|
24
|
+
let(:additional_content) { "#{reserved_bits}this_is_not_part_of_the_lead" }
|
25
|
+
|
26
|
+
it "parses RPM major and minor version numbers" do
|
27
|
+
lead_with(rpm_version).rpm_version_major.must_equal 2
|
28
|
+
lead_with(rpm_version).rpm_version_minor.must_equal 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it "outputs RPM version number if common string format" do
|
32
|
+
lead_with(rpm_version).rpm_version.must_equal "2.1"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "tells if a file is recognized as an RPM or not" do
|
36
|
+
assert lead_with(rpm_version).rpm?,
|
37
|
+
"failed to recognize the file as an RPM"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "recognizes RPM as binary" do
|
41
|
+
assert lead_with(binary_type).binary_type?,
|
42
|
+
"failed to recognize RPM as of binary type"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "recognizes RPM as source" do
|
46
|
+
assert lead_with(source_type).source_type?,
|
47
|
+
"failed to recognize RPM as of source type"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "tells which architecture the package is built for" do
|
51
|
+
lead_with(arch).arch.must_equal "i386/x86_64"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "tells the package name" do
|
55
|
+
lead_with(pkg_name).name.must_equal "awesomepkg-1.0-127.fedora19"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "tells the os for which the package was built" do
|
59
|
+
lead_with(os).os.must_equal "Linux"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "tells whether the package is signed or not" do
|
63
|
+
assert lead_with(signature_type_header).signed?,
|
64
|
+
"expected to recognize the RPM as signed, but it was not"
|
65
|
+
|
66
|
+
refute lead_with(unknown_signature_type).signed?,
|
67
|
+
"expected to recognize the RPM as NOT signed, but it was"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "exposes the reserved bits at the end of the lead" do
|
71
|
+
lead_with(reserved_bits).reserved.length.must_equal 16
|
72
|
+
end
|
73
|
+
|
74
|
+
it "can parse an incoming IO returning itself and the remaining part for
|
75
|
+
subsequent elaboration" do
|
76
|
+
lead, scrap = Rupert::RPM::Lead.chomp(io(additional_content))
|
77
|
+
|
78
|
+
lead.must_be_instance_of Rupert::RPM::Lead
|
79
|
+
scrap.read.must_equal "this_is_not_part_of_the_lead"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when reading an invalid RPM" do
|
84
|
+
let(:invalid_magic) { "\x00\x01\x02\x03" }
|
85
|
+
|
86
|
+
it "recognizes the file is not an RPM" do
|
87
|
+
refute lead_with(invalid_magic).rpm?,
|
88
|
+
"failed to recognize the file as NOT an RPM"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when reading an empty file" do
|
93
|
+
let(:empty_lead) { lead_from "" }
|
94
|
+
|
95
|
+
it "recognizes the file is not an RPM" do
|
96
|
+
refute empty_lead.rpm?,
|
97
|
+
"failed to recognize the file as NOT an RPM"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Rupert::RPM::Signature::Index do
|
4
|
+
let(:entry) { Rupert::RPM::Signature::Entry.new(1234, 7, 567, 890) }
|
5
|
+
let(:entries) { { entry.tag => entry } }
|
6
|
+
let(:store) { mock }
|
7
|
+
let(:index) { Rupert::RPM::Signature::Index.new(entries, store) }
|
8
|
+
|
9
|
+
it "retrieves binary data marked with a specific tag from the store" do
|
10
|
+
store.expects(:fetch).once.with(entry)
|
11
|
+
|
12
|
+
index.get(entry.tag)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Rupert::RPM::Signature::Store do
|
4
|
+
let(:raw_io) { io(ascii("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")) }
|
5
|
+
let(:entry) { Rupert::RPM::Signature::Entry.new(nil, 7, 4, 5) }
|
6
|
+
let(:store) { Rupert::RPM::Signature::Store.new(raw_io) }
|
7
|
+
|
8
|
+
it "fetches a raw chunck of data given the entry that points to it" do
|
9
|
+
data = store.fetch(entry)
|
10
|
+
data.must_equal ascii("\x04\x05\x06\x07\x08")
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Rupert::RPM::Signature do
|
4
|
+
let(:md5_signature_tag) { Rupert::RPM::Signature::MD5_TAG }
|
5
|
+
let(:index) { mock }
|
6
|
+
let(:signature) { Rupert::RPM::Signature.new(index) }
|
7
|
+
let(:pristine_content) { ascii("\x01\x02\x03\x04") }
|
8
|
+
let(:corrupted_content) { ascii("\xf4\x04\x57\x1e") }
|
9
|
+
|
10
|
+
it "fetches the MD5 from its index" do
|
11
|
+
index.expects(:get).once.with(md5_signature_tag)
|
12
|
+
|
13
|
+
signature.md5
|
14
|
+
end
|
15
|
+
|
16
|
+
it "correctly verifies integrity of pristine and corrupted packages" do
|
17
|
+
index.stubs(:get).returns(md5(pristine_content))
|
18
|
+
|
19
|
+
assert signature.verify_checksum(pristine_content),
|
20
|
+
"expected pristine content to be verified correctly, but it was not"
|
21
|
+
|
22
|
+
refute signature.verify_checksum(corrupted_content),
|
23
|
+
"expected corrupted content not to be verified correctly, but it was"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Rupert::RPM do
|
4
|
+
let(:signature) { mock }
|
5
|
+
let(:rpm) { Rupert::RPM.new(nil, signature, signed_content) }
|
6
|
+
let(:signed_content) { ascii("\x01\x02\x03\x04") }
|
7
|
+
|
8
|
+
it "exposes the MD5 digest held by the signature" do
|
9
|
+
signature.expects(:md5).once.returns("abc")
|
10
|
+
|
11
|
+
rpm.md5
|
12
|
+
end
|
13
|
+
|
14
|
+
it "asks the signature to verify content integrity" do
|
15
|
+
signature.expects(:verify_checksum).once.with(signed_content)
|
16
|
+
|
17
|
+
rpm.intact?
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rupert
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stefano Zanella
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-spec-context
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: flog
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: flay
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: "\n Rupert allows to manipulate RPM files independently from availability
|
126
|
+
of rpmlib.\n "
|
127
|
+
email:
|
128
|
+
- zanella.stefano@gmail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files:
|
132
|
+
- README.md
|
133
|
+
files:
|
134
|
+
- .gitignore
|
135
|
+
- .ruby-version
|
136
|
+
- .travis.yml
|
137
|
+
- Gemfile
|
138
|
+
- LICENSE.txt
|
139
|
+
- README.md
|
140
|
+
- Rakefile
|
141
|
+
- TODO.md
|
142
|
+
- lib/rupert.rb
|
143
|
+
- lib/rupert/errors.rb
|
144
|
+
- lib/rupert/parser.rb
|
145
|
+
- lib/rupert/rpm.rb
|
146
|
+
- lib/rupert/rpm/lead.rb
|
147
|
+
- lib/rupert/rpm/signature.rb
|
148
|
+
- lib/rupert/rpm/signature/entry.rb
|
149
|
+
- lib/rupert/rpm/signature/index.rb
|
150
|
+
- lib/rupert/rpm/signature/store.rb
|
151
|
+
- lib/rupert/version.rb
|
152
|
+
- rupert.gemspec
|
153
|
+
- test/end_to_end/rpm_signature_test.rb
|
154
|
+
- test/end_to_end/rpm_test.rb
|
155
|
+
- test/fixtures/README.md
|
156
|
+
- test/fixtures/notanrpm-0.0.1-1.el6.noarch.rpm
|
157
|
+
- test/fixtures/redhat-lsb-4.0-7.el6.centos.src.rpm
|
158
|
+
- test/fixtures/rpm-4.8.0-32.el6.x86_64.rpm
|
159
|
+
- test/fixtures/rpm-libs-4.8.0-32.el6.i686.rpm
|
160
|
+
- test/fixtures/rpm-libs-4.8.0-32.el6.x86_64.rpm
|
161
|
+
- test/fixtures/rpmdevtools-7.5-2.el6.noarch.rpm
|
162
|
+
- test/test_helper.rb
|
163
|
+
- test/unit/rpm/lead_test.rb
|
164
|
+
- test/unit/rpm/signature/index_test.rb
|
165
|
+
- test/unit/rpm/signature/store_test.rb
|
166
|
+
- test/unit/rpm/signature_test.rb
|
167
|
+
- test/unit/rpm_test.rb
|
168
|
+
homepage: ''
|
169
|
+
licenses:
|
170
|
+
- MIT
|
171
|
+
metadata: {}
|
172
|
+
post_install_message:
|
173
|
+
rdoc_options:
|
174
|
+
- --charset=UTF-8
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - '>='
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
requirements: []
|
188
|
+
rubyforge_project:
|
189
|
+
rubygems_version: 2.0.3
|
190
|
+
signing_key:
|
191
|
+
specification_version: 4
|
192
|
+
summary: Pure Ruby RPM Library
|
193
|
+
test_files:
|
194
|
+
- test/end_to_end/rpm_signature_test.rb
|
195
|
+
- test/end_to_end/rpm_test.rb
|
196
|
+
- test/fixtures/README.md
|
197
|
+
- test/fixtures/notanrpm-0.0.1-1.el6.noarch.rpm
|
198
|
+
- test/fixtures/redhat-lsb-4.0-7.el6.centos.src.rpm
|
199
|
+
- test/fixtures/rpm-4.8.0-32.el6.x86_64.rpm
|
200
|
+
- test/fixtures/rpm-libs-4.8.0-32.el6.i686.rpm
|
201
|
+
- test/fixtures/rpm-libs-4.8.0-32.el6.x86_64.rpm
|
202
|
+
- test/fixtures/rpmdevtools-7.5-2.el6.noarch.rpm
|
203
|
+
- test/test_helper.rb
|
204
|
+
- test/unit/rpm/lead_test.rb
|
205
|
+
- test/unit/rpm/signature/index_test.rb
|
206
|
+
- test/unit/rpm/signature/store_test.rb
|
207
|
+
- test/unit/rpm/signature_test.rb
|
208
|
+
- test/unit/rpm_test.rb
|