mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-14 15:53:36 +01:00
updates
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target/
|
||||
674
LICENSE
Normal file
674
LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
14
README
Normal file
14
README
Normal file
@@ -0,0 +1,14 @@
|
||||
About:
|
||||
DependencyCheck is a simple utility that attempts to determine if there is a Common Product Enumeration (CPE) identifier for a given project dependency. If found, it will generate a report linking to the associated CVE entries.
|
||||
|
||||
Usage:
|
||||
Still under development: mvn package site
|
||||
|
||||
TODO:
|
||||
Add CVE download/indexing and CPE lookup.
|
||||
Finish report generation.
|
||||
Fix/finish the CLI.
|
||||
|
||||
Author: Jeremy Long (jeremy.long@gmail.com)
|
||||
|
||||
Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
205
checkstyle-checks.xml
Normal file
205
checkstyle-checks.xml
Normal file
@@ -0,0 +1,205 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
|
||||
|
||||
<!--
|
||||
Checkstyle configuration that checks the maven coding conventions from:
|
||||
-->
|
||||
|
||||
<module name="Checker">
|
||||
|
||||
<!-- Checks that each Java package has a Javadoc file used for commenting. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
|
||||
<module name="JavadocPackage">
|
||||
<!--property name="allowLegacy" value="true"/-->
|
||||
</module>
|
||||
|
||||
<!-- Checks whether files end with a new line. -->
|
||||
<!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
|
||||
<!--module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="system"/>
|
||||
</module-->
|
||||
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="checkstyle-suppressions.xml"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks that property files contain the same keys. -->
|
||||
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
|
||||
<!-- module name="Translation"/ -->
|
||||
|
||||
<module name="FileLength"/>
|
||||
|
||||
<!-- Checks for Headers -->
|
||||
<!-- See http://checkstyle.sf.net/config_header.html -->
|
||||
<module name="RegexpHeader">
|
||||
<property name="headerFile" value="checkstyle-header.txt"/>
|
||||
</module>
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Line with trailing spaces (disabled as it's too noisy) -->
|
||||
<module name="RegexpSingleline">
|
||||
<property name="format" value="\s+$"/>
|
||||
<property name="message" value="Line has trailing spaces."/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
|
||||
<property name="cacheFile" value="${checkstyle.cache.file}"/>
|
||||
|
||||
<property name="tabWidth" value="4"/>
|
||||
|
||||
<module name="LeftCurly">
|
||||
<property name="option" value="eol"/>
|
||||
</module>
|
||||
|
||||
<module name="RightCurly">
|
||||
<!--property name="option" value="alone"/-->
|
||||
</module>
|
||||
|
||||
<module name="LineLength">
|
||||
<property name="max" value="150" />
|
||||
<property name="ignorePattern" value="@version|@see|@todo|TODO"/>
|
||||
</module>
|
||||
|
||||
<module name="MemberName" />
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||
<module name="JavadocMethod">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="scope" value="protected"/>
|
||||
</module>
|
||||
<module name="JavadocType">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="allowUnknownTags" value="true" />
|
||||
</module>
|
||||
<module name="JavadocVariable">
|
||||
<property name="severity" value="info"/>
|
||||
<property name="scope" value="protected"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!-- Checks for Naming Conventions. -->
|
||||
<!-- See http://checkstyle.sf.net/config_naming.html -->
|
||||
<module name="ConstantName"/>
|
||||
<module name="LocalFinalVariableName"/>
|
||||
<module name="LocalVariableName"/>
|
||||
<module name="MethodName"/>
|
||||
<module name="PackageName"/>
|
||||
<module name="ParameterName"/>
|
||||
<module name="StaticVariableName"/>
|
||||
<module name="TypeName"/>
|
||||
|
||||
<!-- Checks for imports -->
|
||||
<!-- See http://checkstyle.sf.net/config_import.html -->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="IllegalImport"/>
|
||||
<module name="RedundantImport"/>
|
||||
<module name="UnusedImports"/>
|
||||
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
||||
<module name="MethodLength"/>
|
||||
<module name="ParameterNumber"/>
|
||||
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||
<module name="EmptyForIteratorPad">
|
||||
<property name="option" value="space"/>
|
||||
</module>
|
||||
<module name="OperatorWrap"/>
|
||||
<!--module name="ParenPad">
|
||||
<property name="option" value="space" />
|
||||
</module-->
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="WhitespaceAround"/>
|
||||
<!-- module name="MethodParamPad"/ -->
|
||||
|
||||
|
||||
<!-- Modifier Checks -->
|
||||
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="RedundantModifier"/>
|
||||
|
||||
|
||||
<!-- Checks for blocks. You know, those {}'s -->
|
||||
<!-- See http://checkstyle.sf.net/config_blocks.html -->
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="text"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
|
||||
|
||||
<!-- Checks for common coding problems -->
|
||||
<!-- See http://checkstyle.sf.net/config_coding.html -->
|
||||
<!-- module name="AvoidInlineConditionals"/ -->
|
||||
<module name="DoubleCheckedLocking"/>
|
||||
<module name="EmptyStatement"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<module name="HiddenField">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="ignoreSetter" value="true"/>
|
||||
<property name="ignoreConstructorParameter" value="true"/>
|
||||
</module>
|
||||
<module name="IllegalInstantiation"/>
|
||||
<module name="InnerAssignment"/>
|
||||
<!--
|
||||
<module name="MagicNumber">
|
||||
<property name="ignoreNumbers" value="-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 14, 32, 64, 100, 128, 256, 512, 1000, 1024, 4096"/>
|
||||
</module>
|
||||
-->
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<!--module name="RedundantThrows"/-->
|
||||
<module name="SimplifyBooleanExpression"/>
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
|
||||
<!-- Checks for class design -->
|
||||
<!-- See http://checkstyle.sf.net/config_design.html -->
|
||||
<!-- module name="DesignForExtension"/ -->
|
||||
<!-- module name="FinalClass"/ -->
|
||||
<module name="HideUtilityClassConstructor"/>
|
||||
<module name="InterfaceIsType"/>
|
||||
<module name="VisibilityModifier">
|
||||
<property name="protectedAllowed" value="true"/>
|
||||
<property name="packageAllowed" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Miscellaneous other checks. -->
|
||||
<!-- See http://checkstyle.sf.net/config_misc.html -->
|
||||
<!-- module name="ArrayTypeStyle"/ -->
|
||||
<!-- module name="FinalParameters"/ -->
|
||||
<!-- Let todo plugin handle this.
|
||||
<module name="TodoComment"/>
|
||||
-->
|
||||
<module name="UpperEll"/>
|
||||
|
||||
</module>
|
||||
|
||||
</module>
|
||||
19
checkstyle-header.txt
Normal file
19
checkstyle-header.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
^package
|
||||
^/\*\s*$
|
||||
^ \* This file is part of DependencyCheck\.\s*$
|
||||
^ \*\s*$
|
||||
^ \* DependencyCheck is free software\: you can redistribute it and/or modify\s*$
|
||||
^ \* it under the terms of the GNU General Public License as published by\s*$
|
||||
^ \* the Free Software Foundation, either version 3 of the License, or\s*$
|
||||
^ \* \(at your option\) any later version\.\s*$
|
||||
^ \*\s*$
|
||||
^ \* DependencyCheck is distributed in the hope that it will be useful,\s*$
|
||||
^ \* but WITHOUT ANY WARRANTY\; without even the implied warranty of\s*$
|
||||
^ \* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\. See the\s*$
|
||||
^ \* GNU General Public License for more details\.\s*$
|
||||
^ \*\s*
|
||||
^ \* You should have received a copy of the GNU General Public License
|
||||
^ \* along with DependencyCheck\. If not, see http://www.gnu.org/licenses/\.\s*$
|
||||
^ \*\s*$
|
||||
^ \* Copyright \(c\) 2012 Jeremy Long. All Rights Reserved\.\s*$
|
||||
^ \*/\s*$
|
||||
15
checkstyle-suppressions.xml
Normal file
15
checkstyle-suppressions.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.0//EN"
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_0.dtd">
|
||||
|
||||
<suppressions>
|
||||
<suppress checks=".*" files=".*[\\/]package-info\.java" />
|
||||
<suppress checks=".*" files=".*org[\\/]codesecure[\\/]dependencycheck[\\/]utils[\\/]SSDeep.java" />
|
||||
<suppress checks=".*" files=".*org[\\/]codesecure[\\/]dependencycheck[\\/]utils[\\/]Filter.java" />
|
||||
<suppress checks=".*" files=".*org[\\/]codesecure[\\/]dependencycheck[\\/]utils[\\/]Checksum.java" />
|
||||
<suppress checks="MagicNumberCheck"
|
||||
files="org[\\/]codesecure[\\/]dependencycheck[\\/]data[\\/]cpe/Entry.java"
|
||||
lines="376"/>
|
||||
</suppressions>
|
||||
396
pom.xml
Normal file
396
pom.xml
Normal file
@@ -0,0 +1,396 @@
|
||||
<!--
|
||||
Copyright (c) 2012 - Jeremy Long
|
||||
|
||||
This file is part of DependencyCheck.
|
||||
|
||||
DependencyCheck is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DependencyCheck is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with DependencyCheck. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.codesecure</groupId>
|
||||
<artifactId>DependencyCheck</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>DependencyCheck</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<description>DependencyCheck is a simple utility that attempts to determine if there is a Common Product Enumeration (CPE) identifier for a given project dependency. If found, it will generate a report linking to the associated CVE entries.</description>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Jeremy Long</name>
|
||||
<email>jeremy.long@gmail.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU General Public License version 3</name>
|
||||
<url>http://www.gnu.org/licenses/</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<configuration>
|
||||
<bottom>Copyright © 2012 Jeremy Long. All Rights Reserved.</bottom>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
<excludeScope>provided</excludeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.codesecure.dependencycheck.App</mainClass>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<configuration>
|
||||
<instrumentation>
|
||||
<ignores>
|
||||
<ignore>*$KEYS.class</ignore>
|
||||
<ignore>*$Element.class</ignore>
|
||||
</ignores>
|
||||
<excludes>
|
||||
<exclude>*$KEYS.class</exclude>
|
||||
<exclude>*$Element.class</exclude>
|
||||
</excludes>
|
||||
</instrumentation>
|
||||
<check>
|
||||
<branchRate>85</branchRate>
|
||||
<lineRate>85</lineRate>
|
||||
<haltOnFailure>false</haltOnFailure>
|
||||
<totalBranchRate>85</totalBranchRate>
|
||||
<totalLineRate>85</totalLineRate>
|
||||
<packageLineRate>85</packageLineRate>
|
||||
<packageBranchRate>85</packageBranchRate>
|
||||
<regexes>
|
||||
<regex>
|
||||
<pattern>org.codesecure.dependencycheck.cpe.CPEEntry</pattern>
|
||||
<branchRate>60</branchRate>
|
||||
<lineRate>60</lineRate>
|
||||
</regex>
|
||||
<regex>
|
||||
<pattern>.*\$.*</pattern>
|
||||
<branchRate>0</branchRate>
|
||||
<lineRate>0</lineRate>
|
||||
</regex>
|
||||
<regex>
|
||||
<pattern>org.codesecure.dependencycheck.store.CPEFields</pattern>
|
||||
<branchRate>0</branchRate>
|
||||
<lineRate>0</lineRate>
|
||||
</regex>
|
||||
|
||||
<regex>
|
||||
<pattern>org.codesecure.dependencycheck.utils.SSDeep</pattern>
|
||||
<branchRate>0</branchRate>
|
||||
<lineRate>0</lineRate>
|
||||
</regex>
|
||||
</regexes>
|
||||
</check>
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>net.sourceforge.cobertura.datafile</name>
|
||||
<value>${project.build.directory}/cobertura/cobertura.ser</value>
|
||||
<workingDirectory>target</workingDirectory>
|
||||
</property>
|
||||
<property>
|
||||
<name>index.cpe</name>
|
||||
<value>${project.build.directory}/store/cpe</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>index.osvdb</name>
|
||||
<value>${project.build.directory}/store/osvdb</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>index.cpe</name>
|
||||
<value>${project.build.directory}/store/cpe</value>
|
||||
</property>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<reportPlugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<version>2.12.2</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>report-only</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>2.7.1</version>
|
||||
<configuration>
|
||||
<targetJdk>1.6</targetJdk>
|
||||
<linkXref>true</linkXref>
|
||||
<sourceEncoding>utf-8</sourceEncoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>default</id>
|
||||
<reports>
|
||||
<report>javadoc</report><!-- using this to exclude "test-javadoc", I just don't see the point of generating this -->
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<enableRulesSummary>false</enableRulesSummary>
|
||||
<configLocation>checkstyle-checks.xml</configLocation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</reportPlugins>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.2</version>
|
||||
<classifier>javadoc</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.2</version>
|
||||
<classifier>sources</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.2</version>
|
||||
<scope>test</scope>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>3.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<classifier>sources</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<classifier>javadoc</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.6</version>
|
||||
<classifier>javadoc</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.6</version>
|
||||
<classifier>sources</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<classifier>javadoc</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<classifier>sources</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity</artifactId>
|
||||
<version>1.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity</artifactId>
|
||||
<version>1.7</version>
|
||||
<classifier>javadoc</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity</artifactId>
|
||||
<version>1.7</version>
|
||||
<classifier>sources</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-tools</artifactId>
|
||||
<version>2.0</version>
|
||||
<!-- very limited use of the velocity-tools, not all of the dependencies are needed-->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-chain</groupId>
|
||||
<artifactId>commons-chain</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-validator</groupId>
|
||||
<artifactId>commons-validator</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>dom4j</groupId>
|
||||
<artifactId>dom4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>sslext</groupId>
|
||||
<artifactId>sslext</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>antlr</groupId>
|
||||
<artifactId>antlr</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts-taglib</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts-tiles</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-tools</artifactId>
|
||||
<version>2.0</version>
|
||||
<classifier>javadoc</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
118
src/main/java/org/codesecure/dependencycheck/App.java
Normal file
118
src/main/java/org/codesecure/dependencycheck/App.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package org.codesecure.dependencycheck;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.prefs.Preferences;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.codesecure.dependencycheck.data.cpe.CPEQuery;
|
||||
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
|
||||
import org.codesecure.dependencycheck.reporting.ReportGenerator;
|
||||
import org.codesecure.dependencycheck.scanner.Dependency;
|
||||
import org.codesecure.dependencycheck.scanner.Scanner;
|
||||
import org.codesecure.dependencycheck.utils.CliParser;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/*
|
||||
* This file is part of App.
|
||||
*
|
||||
* App is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* App is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with App. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class App {
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
//Preferences.systemRoot().put("java.util.logging.config.file", "log.properties");
|
||||
App app = new App();
|
||||
app.run(args);
|
||||
}
|
||||
|
||||
public void run(String[] args) {
|
||||
CliParser cli = new CliParser();
|
||||
try {
|
||||
cli.parse(args);
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return;
|
||||
} catch (ParseException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cli.isGetVersion()) {
|
||||
cli.printVersionInfo();
|
||||
} else if (cli.isLoadCPE()) {
|
||||
loadCPE(cli.getCpeFile());
|
||||
} else if (cli.isRunScan()) {
|
||||
runScan(cli.getReportDirectory(), cli.getApplicationName(), cli.getScanFiles());
|
||||
} else {
|
||||
cli.printHelp();
|
||||
}
|
||||
|
||||
}
|
||||
private void loadCPE(String cpePath) {
|
||||
try {
|
||||
Importer.importXML(cpePath);
|
||||
} catch (ParserConfigurationException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (SAXException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
private void runScan(String reportDirectory, String applicationName, String[] files) {
|
||||
try {
|
||||
Scanner scanner = new Scanner();
|
||||
for (String file : files) {
|
||||
scanner.scan(file);
|
||||
}
|
||||
List<Dependency> dependencies = scanner.getDependencies();
|
||||
CPEQuery query = new CPEQuery();
|
||||
query.open();
|
||||
for (Dependency d : dependencies) {
|
||||
query.determineCPE(d);
|
||||
}
|
||||
query.close();
|
||||
ReportGenerator report = new ReportGenerator();
|
||||
try {
|
||||
report.generateReports(reportDirectory, applicationName, dependencies);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (org.apache.lucene.queryParser.ParseException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package org.codesecure.dependencycheck.data;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>Lucene utils is a set of utilitize written to make constructing
|
||||
* Lucene queries simplier.</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public final class LuceneUtils {
|
||||
|
||||
/**
|
||||
* Provate contructor as this is a utility class.
|
||||
*/
|
||||
private LuceneUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the text to the supplied StringBuilder escaping Lucene control
|
||||
* characters in the process.
|
||||
*
|
||||
* @param buf a StringBuilder to append the escaped text to
|
||||
* @param text the data to be escaped
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
public static void appendEscapedLuceneQuery(StringBuilder buf,
|
||||
final CharSequence text) {
|
||||
|
||||
if (text == null || buf == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
switch (c) {
|
||||
case '+':
|
||||
case '-':
|
||||
case '&':
|
||||
case '|':
|
||||
case '!':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '[':
|
||||
case ']':
|
||||
case '^':
|
||||
case '"':
|
||||
case '~':
|
||||
case '*':
|
||||
case '?':
|
||||
case ':':
|
||||
case '\\':
|
||||
buf.append('\\');
|
||||
default:
|
||||
buf.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the text passed in so that it is treated as data instead of
|
||||
* control characters.
|
||||
*
|
||||
* @param text data to be escaped
|
||||
* @return the escaped text.
|
||||
*/
|
||||
public static String escapeLuceneQuery(final CharSequence text) {
|
||||
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int size = text.length();
|
||||
size = size >> 1;
|
||||
StringBuilder buf = new StringBuilder(size);
|
||||
|
||||
appendEscapedLuceneQuery(buf, text);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.codesecure.dependencycheck.data;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* VersionAnalyzer is a Lucene Analyzer used to analyze version information.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class VersionAnalyzer {
|
||||
//TODO Implement this...
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.queryParser.ParseException;
|
||||
import org.apache.lucene.queryParser.QueryParser;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.Version;
|
||||
import org.codesecure.dependencycheck.data.LuceneUtils;
|
||||
import org.codesecure.dependencycheck.scanner.Dependency;
|
||||
import org.codesecure.dependencycheck.scanner.Evidence.Confidence;
|
||||
|
||||
/**
|
||||
* CPEQuery is a utility class that takes a project dependency and attempts
|
||||
* to decern if there is an associated CPE. It uses the evidence contained
|
||||
* within the dependency to search the Lucene index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class CPEQuery {
|
||||
|
||||
/**
|
||||
* The maximum number of query results to return.
|
||||
*/
|
||||
static final int MAX_QUERY_RESULTS = 10;
|
||||
/**
|
||||
* The weighting boost to give terms when constructing the Lucene query.
|
||||
*/
|
||||
static final String WEIGHTING_BOOST = "^5";
|
||||
/**
|
||||
* A string representation of a regular expression defining characters
|
||||
* utilized within the CPE Names.
|
||||
*/
|
||||
static final String CLEANSE_CHARACTER_RX = "[^A-Za-z0-9 _-]";
|
||||
/* A string representation of a regular expression used to remove all but
|
||||
* alpha characters.
|
||||
*/
|
||||
static final String CLEANSE_NONALPHA_RX = "[^A-Za-z]*";
|
||||
/**
|
||||
* The additional size to add to a new StringBuilder to account for extra
|
||||
* data that will be written into the string.
|
||||
*/
|
||||
static final int STRING_BUILDER_BUFFER = 20;
|
||||
/**
|
||||
* The Lucene IndexReader.
|
||||
*/
|
||||
private IndexReader indexReader = null;
|
||||
/**
|
||||
* The Lucene IndexSearcher.
|
||||
*/
|
||||
private IndexSearcher indexSearcher = null;
|
||||
/**
|
||||
* The Lucene directory.
|
||||
*/
|
||||
private Directory directory = null;
|
||||
/**
|
||||
* The Lucene Analyzer.
|
||||
*/
|
||||
private Analyzer analyzer = null;
|
||||
/**
|
||||
* The Lucene QueryParser.
|
||||
*/
|
||||
private QueryParser queryParser = null;
|
||||
/**
|
||||
* Indicates whether or not the Lucene Index is open.
|
||||
*/
|
||||
private boolean indexOpen = false;
|
||||
|
||||
/**
|
||||
* Opens the data source.
|
||||
*
|
||||
* @throws IOException when the Lucene directory to be querried does not exist or is corrupt.
|
||||
*/
|
||||
public void open() throws IOException {
|
||||
directory = Index.getDirectory();
|
||||
indexReader = IndexReader.open(directory, true);
|
||||
indexSearcher = new IndexSearcher(indexReader);
|
||||
analyzer = Index.createAnalyzer(); //use the same analyzer as used when indexing
|
||||
//TITLE is the default field because it contains venddor, product, and version all in one.
|
||||
queryParser = new QueryParser(Version.LUCENE_35, Fields.TITLE, analyzer);
|
||||
indexOpen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the data source.
|
||||
*/
|
||||
public void close() {
|
||||
analyzer.close();
|
||||
analyzer = null;
|
||||
queryParser = null;
|
||||
try {
|
||||
indexSearcher.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CPEQuery.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
indexSearcher = null;
|
||||
try {
|
||||
indexReader.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CPEQuery.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
indexReader = null;
|
||||
try {
|
||||
directory.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CPEQuery.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
directory = null;
|
||||
indexOpen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the data source - is the index open.
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return indexOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the Lucene index is closed.
|
||||
* @throws Throwable when a throwable is thrown.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
if (indexOpen) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the data store of CPE entries, trying to identify the CPE for the given
|
||||
* dependency based on the evidence contained within. The depencency passed in is
|
||||
* updated with any identified CPE values.
|
||||
*
|
||||
* @param dependency the dependency to search for CPE entries on.
|
||||
* @throws CorruptIndexException is thrown when the Lucene index is corrupt.
|
||||
* @throws IOException is thrown when an IOException occurs.
|
||||
* @throws ParseException is thrown when the Lucene query cannot be parsed.
|
||||
*/
|
||||
public void determineCPE(Dependency dependency) throws CorruptIndexException, IOException, ParseException {
|
||||
Confidence vendorConf = Confidence.HIGH;
|
||||
Confidence titleConf = Confidence.HIGH;
|
||||
Confidence versionConf = Confidence.HIGH;
|
||||
|
||||
String vendors = dependency.getVendorEvidence().toString(vendorConf);
|
||||
// if ("".equals(vendors)) {
|
||||
// vendors = STRING_THAT_WILL_NEVER_BE_IN_THE_INDEX;
|
||||
// }
|
||||
String titles = dependency.getTitleEvidence().toString(titleConf);
|
||||
// if ("".equals(titles)) {
|
||||
// titles = STRING_THAT_WILL_NEVER_BE_IN_THE_INDEX;
|
||||
// }
|
||||
String versions = dependency.getVersionEvidence().toString(versionConf);
|
||||
// if ("".equals(versions)) {
|
||||
// versions = STRING_THAT_WILL_NEVER_BE_IN_THE_INDEX;
|
||||
// }
|
||||
|
||||
boolean found = false;
|
||||
int cnt = 0;
|
||||
do {
|
||||
List<Entry> entries = searchCPE(vendors, titles, versions, dependency.getTitleEvidence().getWeighting(),
|
||||
dependency.getVendorEvidence().getWeighting());
|
||||
|
||||
if (entries.size()>0) {
|
||||
List<String> verified = verifyEntries(entries, dependency);
|
||||
if (verified.size() > 0) {
|
||||
found = true;
|
||||
dependency.setCPEs(verified);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
int round = cnt % 3;
|
||||
if (round == 0) {
|
||||
vendorConf = reduceConfidence(vendorConf);
|
||||
if (dependency.getVendorEvidence().contains(vendorConf)) {
|
||||
vendors += " " + dependency.getVendorEvidence().toString(vendorConf);
|
||||
} else {
|
||||
cnt += 1;
|
||||
round += 1;
|
||||
}
|
||||
}
|
||||
if (round == 1) {
|
||||
titleConf = reduceConfidence(titleConf);
|
||||
if (dependency.getTitleEvidence().contains(titleConf)) {
|
||||
titles += " " + dependency.getTitleEvidence().toString(titleConf);
|
||||
} else {
|
||||
cnt += 1;
|
||||
round += 1;
|
||||
}
|
||||
}
|
||||
if (round == 2) {
|
||||
versionConf = reduceConfidence(versionConf);
|
||||
if (dependency.getVersionEvidence().contains(versionConf)) {
|
||||
versions += " " + dependency.getVersionEvidence().toString(versionConf);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} while (!found && (++cnt) < 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the given confidence by one level. This returns LOW if the confidence
|
||||
* passed in is not HIGH.
|
||||
*
|
||||
* @param c the confidence to reduce.
|
||||
* @return One less then the confidence passed in.
|
||||
*/
|
||||
private Confidence reduceConfidence(final Confidence c) {
|
||||
if (c == Confidence.HIGH) {
|
||||
return Confidence.MEDIUM;
|
||||
} else {
|
||||
return Confidence.LOW;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the Lucene CPE index to identify possible CPE entries associated
|
||||
* with the supplied vendor, product, and version.
|
||||
*
|
||||
* @param vendor the text used to search the vendor field.
|
||||
* @param product the text used to search the title field.
|
||||
* @param version the text used to search the version field.
|
||||
* @return a list of possible CPE values.
|
||||
* @throws CorruptIndexException when the Lucene index is corrupt.
|
||||
* @throws IOException when the Lucene index is not found.
|
||||
* @throws ParseException when the generated query is not valid.
|
||||
*/
|
||||
protected List<Entry> searchCPE(String vendor, String product, String version)
|
||||
throws CorruptIndexException, IOException, ParseException {
|
||||
return searchCPE(vendor, product, version, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Searches the Lucene CPE index to identify possible CPE entries associated with
|
||||
* the supplied vendor, product, and version.</p>
|
||||
*
|
||||
* <p>If either the vendorWeightings or productWeightings lists have been populated
|
||||
* this data is used to add weighting factors to the search.</p>
|
||||
*
|
||||
* @param vendor the text used to search the vendor field.
|
||||
* @param product the text used to search the title field.
|
||||
* @param version the text used to search the version field.
|
||||
* @param vendorWeightings a list of strings to use to add weighting factors to the vendor field.
|
||||
* @param productWeightings Adds a list of strings that will be used to add weighting factors to the title search.
|
||||
* @return a list of possible CPE values.
|
||||
* @throws CorruptIndexException when the Lucene index is corrupt.
|
||||
* @throws IOException when the Lucene index is not found.
|
||||
* @throws ParseException when the generated query is not valid.
|
||||
*/
|
||||
protected List<Entry> searchCPE(String vendor, String product, String version,
|
||||
List<String> vendorWeightings, List<String> productWeightings)
|
||||
throws CorruptIndexException, IOException, ParseException {
|
||||
ArrayList<Entry> ret = new ArrayList<Entry>(MAX_QUERY_RESULTS);
|
||||
|
||||
String searchString = buildSearch(vendor, product, version, vendorWeightings, productWeightings);
|
||||
if (searchString == null) {
|
||||
return ret;
|
||||
}
|
||||
Query query = queryParser.parse(searchString);
|
||||
TopDocs docs = indexSearcher.search(query, MAX_QUERY_RESULTS);
|
||||
for (ScoreDoc d : docs.scoreDocs) {
|
||||
Document doc = indexSearcher.doc(d.doc);
|
||||
Entry entry = Entry.parse(doc);
|
||||
entry.setSearchScore(d.score);
|
||||
if (!ret.contains(entry)) {
|
||||
ret.add(entry);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Builds a Lucene search string by properly escaping data and constructing a valid search query.</p>
|
||||
*
|
||||
* <p>If either the possibleVendor or possibleProducts lists have been populated this
|
||||
* data is used to add weighting factors to the search string generated.</p>
|
||||
*
|
||||
* @param vendor text to search the vendor field.
|
||||
* @param product text to search the title field.
|
||||
* @param version text to search the version field.
|
||||
* @param vendorWeighting a list of strings to apply to the vendor
|
||||
* to boost the terms weight.
|
||||
* @param produdctWeightings a list of strings to apply to the product/title
|
||||
* to boost the terms weight.
|
||||
* @return the Lucene query.
|
||||
*/
|
||||
protected String buildSearch(String vendor, String product, String version,
|
||||
List<String> vendorWeighting, List<String> produdctWeightings) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(vendor.length() + product.length()
|
||||
+ version.length() + Fields.PRODUCT.length() + Fields.VERSION.length()
|
||||
+ Fields.VENDOR.length() + STRING_BUILDER_BUFFER);
|
||||
|
||||
if ("".equals(version)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!appendWeightedSearch(sb, Fields.PRODUCT, product.toLowerCase(), produdctWeightings)) {
|
||||
return null;
|
||||
}
|
||||
if (!appendWeightedSearch(sb, Fields.VENDOR, vendor.toLowerCase(), vendorWeighting)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
sb.append(Fields.VERSION).append(":(");
|
||||
if (sb.indexOf("^") > 0) {
|
||||
//if we have a weighting on something else, reduce the weighting on the version a lot
|
||||
for (String v : version.split(" ")) {
|
||||
LuceneUtils.appendEscapedLuceneQuery(sb, v);
|
||||
sb.append("^0.2 ");
|
||||
}
|
||||
} else {
|
||||
LuceneUtils.appendEscapedLuceneQuery(sb, version);
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method constructs a Lucene query for a given field. The searchText
|
||||
* is split into seperate words and if the word is within the list of weighted
|
||||
* words then an additional weighting is applied to the term as it is appended
|
||||
* into the query.
|
||||
*
|
||||
* @param sb a StringBuilder that the query text will be appended to.
|
||||
* @param field the field within the Lucene index that the query is searching.
|
||||
* @param searchText text used to construct the query.
|
||||
* @param weightedText a list of terms that will be considered higher
|
||||
* importance when searching.
|
||||
* @return if the append was successful.
|
||||
*/
|
||||
private boolean appendWeightedSearch(StringBuilder sb, String field, String searchText, List<String> weightedText) {
|
||||
//TODO add a mutator or special analyzer that combines words next to each other and adds them as a key.
|
||||
sb.append(" ").append(field).append(":( ");
|
||||
|
||||
String cleanText = cleanseText(searchText);
|
||||
|
||||
if ("".equals(cleanText)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (weightedText == null || weightedText.isEmpty()) {
|
||||
LuceneUtils.appendEscapedLuceneQuery(sb, cleanText);
|
||||
} else {
|
||||
String[] text = cleanText.split("\\s");
|
||||
for (String word : text) {
|
||||
String temp = null;
|
||||
for (String weighted : weightedText) {
|
||||
String weightedStr = cleanseText(weighted);
|
||||
if (equalsIgnoreCaseAndNonAlpha(word, weightedStr)) {
|
||||
temp = LuceneUtils.escapeLuceneQuery(word) + WEIGHTING_BOOST;
|
||||
if (!word.equalsIgnoreCase(weightedStr)) {
|
||||
temp += " " + LuceneUtils.escapeLuceneQuery(weightedStr) + WEIGHTING_BOOST;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (temp == null) {
|
||||
temp = LuceneUtils.escapeLuceneQuery(word);
|
||||
}
|
||||
sb.append(" ").append(temp);
|
||||
}
|
||||
}
|
||||
sb.append(" ) ");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes characters from the input text that are not used within the CPE index.
|
||||
*
|
||||
* @param text is the text to remove the characters from.
|
||||
* @return the text having removed some characters.
|
||||
*/
|
||||
private String cleanseText(String text) {
|
||||
return text.replaceAll(CLEANSE_CHARACTER_RX, " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two strings after lower casing them and removing the non-alpha
|
||||
* characters.
|
||||
*
|
||||
* @param l string one to compare.
|
||||
* @param r string two to compare.
|
||||
* @return whether or not the two strings are similiar.
|
||||
*/
|
||||
private boolean equalsIgnoreCaseAndNonAlpha(String l, String r) {
|
||||
if (l == null || r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String left = l.replaceAll(CLEANSE_NONALPHA_RX, "");
|
||||
String right = r.replaceAll(CLEANSE_NONALPHA_RX, "");
|
||||
return left.equalsIgnoreCase(right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of entries and a dependency. If the entry has terms that were
|
||||
* used (i.e. this CPE entry wasn't identified because the version matched
|
||||
* but the product and title did not) then the CPE Entry is returned in a list
|
||||
* of possible CPE Entries.
|
||||
*
|
||||
* @param entries a list of CPE entries.
|
||||
* @param dependency the dependency that the CPE entries could be for.
|
||||
* @return a list of matched CPE entries.
|
||||
*/
|
||||
private List<String> verifyEntries(final List<Entry> entries, final Dependency dependency) {
|
||||
List<String> verified = new ArrayList<String>();
|
||||
for (Entry e : entries) {
|
||||
if (dependency.getTitleEvidence().containsUsedString(e.getProduct())
|
||||
&& dependency.getVendorEvidence().containsUsedString(e.getVendor())) {
|
||||
//TODO - determine if this is right? Should we be carrying too much about the
|
||||
// version at this point? Likely need to implement the versionAnalyzer....
|
||||
if (dependency.getVersionEvidence().containsUsedString(e.getVersion())) {
|
||||
verified.add(e.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return verified;
|
||||
}
|
||||
}
|
||||
380
src/main/java/org/codesecure/dependencycheck/data/cpe/Entry.java
Normal file
380
src/main/java/org/codesecure/dependencycheck/data/cpe/Entry.java
Normal file
@@ -0,0 +1,380 @@
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.document.Document;
|
||||
|
||||
/**
|
||||
* A single CPE entry from the cpe.xml downloaded from
|
||||
* <a href="http://nvd.nist.gov/cpe.cfm">http://nvd.nist.gov/cpe.cfm</a>.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Entry {
|
||||
|
||||
/**
|
||||
* This parse method does not fully convert a Lucene Document into a CPE Entry;
|
||||
* it only sets the Entry.Name.
|
||||
*
|
||||
* @param doc a Lucene Document.
|
||||
* @return a CPE Entry.
|
||||
*/
|
||||
public static Entry parse(Document doc) {
|
||||
Entry entry = new Entry();
|
||||
try {
|
||||
entry.setName(doc.get(Fields.NAME));
|
||||
entry.setTitle(doc.get(Fields.TITLE));
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
Logger.getLogger(Entry.class.getName()).log(Level.SEVERE, null, ex);
|
||||
entry.name = doc.get(Fields.NAME);
|
||||
}
|
||||
// entry.vendor = doc.get(Fields.VENDOR);
|
||||
// entry.version = doc.get(Fields.VERSION);
|
||||
// //entry.revision = doc.get(Fields.REVISION);
|
||||
// entry.product = doc.get(Fields.TITLE);
|
||||
// entry.nvdId = doc.get(Fields.NVDID);
|
||||
return entry;
|
||||
}
|
||||
/**
|
||||
* The title of the CPE
|
||||
*/
|
||||
protected String title;
|
||||
|
||||
/**
|
||||
* Get the value of title
|
||||
*
|
||||
* @return the value of title
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of title
|
||||
*
|
||||
* @param title new value of title
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
/**
|
||||
* The name of the CPE entry.
|
||||
*/
|
||||
protected String name;
|
||||
|
||||
/**
|
||||
* Get the value of name
|
||||
*
|
||||
* @return the value of name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of name and calls parseName to obtain the vendor:product:version:revision
|
||||
*
|
||||
* @param name new value of name
|
||||
* @throws UnsupportedEncodingException should never be thrown...
|
||||
*/
|
||||
public void setName(String name) throws UnsupportedEncodingException {
|
||||
this.name = name;
|
||||
parseName();
|
||||
}
|
||||
/**
|
||||
* The status of the CPE Entry.
|
||||
*/
|
||||
protected String status;
|
||||
|
||||
/**
|
||||
* Get the value of status
|
||||
*
|
||||
* @return the value of status
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of status
|
||||
*
|
||||
* @param status new value of status
|
||||
*/
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
/**
|
||||
* The modification date of the CPE Entry.
|
||||
*/
|
||||
protected Date modificationDate;
|
||||
|
||||
/**
|
||||
* Get the value of modificationDate
|
||||
*
|
||||
* @return the value of modificationDate
|
||||
*/
|
||||
public Date getModificationDate() {
|
||||
return modificationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of modificationDate
|
||||
*
|
||||
* @param modificationDate new value of modificationDate
|
||||
*/
|
||||
public void setModificationDate(Date modificationDate) {
|
||||
this.modificationDate = modificationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of modificationDate
|
||||
*
|
||||
* Expected format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
|
||||
*
|
||||
* @param modificationDate new value of modificationDate
|
||||
* @throws ParseException is thrown when a parse exception occurs.
|
||||
*/
|
||||
public void setModificationDate(String modificationDate) throws ParseException {
|
||||
|
||||
String formatStr = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
||||
Date tempDate = null;
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
|
||||
sdf.setLenient(true);
|
||||
tempDate = sdf.parse(modificationDate);
|
||||
|
||||
this.modificationDate = tempDate;
|
||||
}
|
||||
/**
|
||||
* The nvdId.
|
||||
*/
|
||||
protected String nvdId;
|
||||
|
||||
/**
|
||||
* Get the value of nvdId
|
||||
*
|
||||
* @return the value of nvdId
|
||||
*/
|
||||
public String getNvdId() {
|
||||
return nvdId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of nvdId
|
||||
*
|
||||
* @param nvdId new value of nvdId
|
||||
*/
|
||||
public void setNvdId(String nvdId) {
|
||||
this.nvdId = nvdId;
|
||||
}
|
||||
/**
|
||||
* The vendor name.
|
||||
*/
|
||||
protected String vendor;
|
||||
|
||||
/**
|
||||
* Get the value of vendor
|
||||
*
|
||||
* @return the value of vendor
|
||||
*/
|
||||
public String getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of vendor
|
||||
*
|
||||
* @param vendor new value of vendor
|
||||
*/
|
||||
public void setVendor(String vendor) {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
/**
|
||||
* The product name.
|
||||
*/
|
||||
protected String product;
|
||||
|
||||
/**
|
||||
* Get the value of product
|
||||
*
|
||||
* @return the value of product
|
||||
*/
|
||||
public String getProduct() {
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of product
|
||||
*
|
||||
* @param product new value of product
|
||||
*/
|
||||
public void setProduct(String product) {
|
||||
this.product = product;
|
||||
}
|
||||
/**
|
||||
* The product version.
|
||||
*/
|
||||
protected String version;
|
||||
|
||||
/**
|
||||
* Get the value of version
|
||||
*
|
||||
* @return the value of version
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of version
|
||||
*
|
||||
* @param version new value of version
|
||||
*/
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
/**
|
||||
* The product revision.
|
||||
*/
|
||||
protected String revision;
|
||||
|
||||
/**
|
||||
* Get the value of revision
|
||||
*
|
||||
* @return the value of revision
|
||||
*/
|
||||
public String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of revision
|
||||
*
|
||||
* @param revision new value of revision
|
||||
*/
|
||||
public void setRevision(String revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
/**
|
||||
* If the CPE Entry is well known (i.e. based off a hash)
|
||||
*/
|
||||
protected boolean wellKnown = false;
|
||||
|
||||
/**
|
||||
* Get the value of wellKnown
|
||||
*
|
||||
* @return the value of wellKnown
|
||||
*/
|
||||
public boolean isWellKnown() {
|
||||
return wellKnown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of wellKnown
|
||||
*
|
||||
* @param wellKnown new value of wellKnown
|
||||
*/
|
||||
public void setWellKnown(boolean wellKnown) {
|
||||
this.wellKnown = wellKnown;
|
||||
}
|
||||
/**
|
||||
* The search score.
|
||||
*/
|
||||
protected float searchScore;
|
||||
|
||||
/**
|
||||
* Get the value of searchScore
|
||||
*
|
||||
* @return the value of searchScore
|
||||
*/
|
||||
public float getSearchScore() {
|
||||
return searchScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of searchScore
|
||||
*
|
||||
* @param searchScore new value of searchScore
|
||||
*/
|
||||
public void setSearchScore(float searchScore) {
|
||||
this.searchScore = searchScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Parses a name attribute value, from the cpe.xml, into its
|
||||
* corresponding parts: vendor, product, version, revision.</p>
|
||||
* <p>Example:</p>
|
||||
* <code> cpe:/a:apache:struts:1.1:rc2</code>
|
||||
*
|
||||
* <p>Results in:</p>
|
||||
* <ul>
|
||||
* <li>Vendor: apache</li>
|
||||
* <li>Product: struts</li>
|
||||
* <li>Version: 1.1</li>
|
||||
* <li>Revision: rc2</li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws UnsupportedEncodingException should never be thrown...
|
||||
*/
|
||||
private void parseName() throws UnsupportedEncodingException {
|
||||
if (name != null && name.length() > 7) {
|
||||
String[] data = name.substring(7).split(":");
|
||||
if (data.length >= 1) {
|
||||
vendor = URLDecoder.decode(data[0], "UTF-8");
|
||||
if (data.length >= 2) {
|
||||
product = URLDecoder.decode(data[1], "UTF-8");
|
||||
if (data.length >= 3) {
|
||||
version = URLDecoder.decode(data[2], "UTF-8");
|
||||
if (data.length >= 4) {
|
||||
revision = URLDecoder.decode(data[3], "UTF-8");
|
||||
}
|
||||
//ignore edition and language fields.. don't really see them used in the a:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Entry other = (Entry) obj;
|
||||
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 83 * hash + (this.name != null ? this.name.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fields is a collection of field names used within the Lucene index for CPE
|
||||
* entries.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public abstract class Fields {
|
||||
/**
|
||||
* The key for the name field.
|
||||
*/
|
||||
public static final String NAME = "name";
|
||||
/**
|
||||
* The key for the vendor field.
|
||||
*/
|
||||
public static final String VENDOR = "vendor";
|
||||
/**
|
||||
* The key for the version field.
|
||||
*/
|
||||
public static final String VERSION = "version";
|
||||
//public static final String REVISION = "revision";
|
||||
/**
|
||||
* The key for the product field.
|
||||
*/
|
||||
public static final String PRODUCT = "product";
|
||||
/**
|
||||
* The key for the title field. This is a field combining vendor, product, and version.
|
||||
*/
|
||||
public static final String TITLE = "title";
|
||||
/**
|
||||
* The key for the nvdId field.
|
||||
*/
|
||||
public static final String NVDID = "nvdid";
|
||||
}
|
||||
143
src/main/java/org/codesecure/dependencycheck/data/cpe/Index.java
Normal file
143
src/main/java/org/codesecure/dependencycheck/data/cpe/Index.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.KeywordAnalyzer;
|
||||
import org.apache.lucene.analysis.PerFieldAnalyzerWrapper;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
import org.apache.lucene.util.Version;
|
||||
import org.codesecure.dependencycheck.utils.Downloader;
|
||||
import org.codesecure.dependencycheck.utils.Settings;
|
||||
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
|
||||
|
||||
/**
|
||||
* The Index class is used to utilize and maintain the CPE Index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Index {
|
||||
|
||||
/**
|
||||
* The Lucene directory containing the index.
|
||||
*/
|
||||
protected Directory directory = null;
|
||||
/**
|
||||
* The IndexWriter for the Lucene index.
|
||||
*/
|
||||
protected IndexWriter indexWriter = null;
|
||||
|
||||
/**
|
||||
* Opens the CPE Index.
|
||||
* @throws IOException is thrown if an IOException occurs opening the index.
|
||||
*/
|
||||
public void open() throws IOException {
|
||||
directory = Index.getDirectory();
|
||||
|
||||
Analyzer analyzer = Index.createAnalyzer(); //new StandardAnalyzer(Version.LUCENE_35);
|
||||
IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_35, analyzer);
|
||||
indexWriter = new IndexWriter(directory, conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CPE Index.
|
||||
*
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt.
|
||||
* @throws IOException is thrown if an IOException occurs closing the index.
|
||||
*/
|
||||
public void close() throws CorruptIndexException, IOException {
|
||||
indexWriter.commit();
|
||||
indexWriter.close(true);
|
||||
directory.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory that holds the CPE Index.
|
||||
*
|
||||
* @return the Directory containing the CPE Index.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
public static Directory getDirectory() throws IOException {
|
||||
String fileName = Settings.getString(Settings.KEYS.CPE_INDEX);
|
||||
File path = new File(fileName);
|
||||
Directory directory = FSDirectory.open(path);
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Analyzer for the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
public static Analyzer createAnalyzer() {
|
||||
Map fieldAnalyzers = new HashMap();
|
||||
|
||||
fieldAnalyzers.put(Fields.VERSION, new KeywordAnalyzer());
|
||||
//new WhitespaceAnalyzer(Version.LUCENE_35)); //
|
||||
|
||||
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(
|
||||
new StandardAnalyzer(Version.LUCENE_35), fieldAnalyzers);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the latest CPE XML file from the web and imports it into
|
||||
* the current CPE Index.
|
||||
*
|
||||
* @throws Exception is thrown if an exception occurs.
|
||||
*/
|
||||
public void updateIndexFromWeb() throws Exception {
|
||||
if (updateNeeded()) {
|
||||
URL url = new URL(Settings.getString(Settings.KEYS.CPE_URL));
|
||||
File outputPath = null;
|
||||
try {
|
||||
outputPath = File.createTempFile("cpe", ".xml");
|
||||
Downloader.fetchFile(url, outputPath);
|
||||
Importer.importXML(outputPath.toString());
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
outputPath.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the index needs to be updated.
|
||||
*
|
||||
* @return whether or not the CPE Index needs to be updated.
|
||||
*/
|
||||
public boolean updateNeeded() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.FieldInfo.IndexOptions;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.codesecure.dependencycheck.data.LuceneUtils;
|
||||
import org.codesecure.dependencycheck.data.cpe.xml.EntrySaveDelegate;
|
||||
|
||||
/**
|
||||
* The Indexer is used to convert a CPE Entry, retrieved from the CPE XML file,
|
||||
* into a Document that is stored in the Lucene index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Indexer extends Index implements EntrySaveDelegate {
|
||||
|
||||
/**
|
||||
* Saves a CPE Entry into the Lucene index.
|
||||
*
|
||||
* @param entry a CPE entry.
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
public void saveEntry(Entry entry) throws CorruptIndexException, IOException {
|
||||
Document doc = convertEntryToDoc(entry);
|
||||
Term term = new Term(Fields.NVDID, LuceneUtils.escapeLuceneQuery(entry.getNvdId()));
|
||||
indexWriter.updateDocument(term, doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converst a CPE entry into a Lucene Document.
|
||||
*
|
||||
* @param entry a CPE Entry.
|
||||
* @return a Lucene Document containing a CPE Entry.
|
||||
*/
|
||||
protected Document convertEntryToDoc(Entry entry) {
|
||||
Document doc = new Document();
|
||||
|
||||
Field name = new Field(Fields.NAME, entry.getName(), Field.Store.YES, Field.Index.ANALYZED);
|
||||
name.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
doc.add(name);
|
||||
|
||||
Field nvdId = new Field(Fields.NVDID, entry.getNvdId(), Field.Store.NO, Field.Index.ANALYZED);
|
||||
nvdId.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
doc.add(nvdId);
|
||||
|
||||
Field vendor = new Field(Fields.VENDOR, entry.getVendor(), Field.Store.NO, Field.Index.ANALYZED);
|
||||
vendor.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
vendor.setBoost(5.0F);
|
||||
doc.add(vendor);
|
||||
|
||||
Field product = new Field(Fields.PRODUCT, entry.getProduct(), Field.Store.NO, Field.Index.ANALYZED);
|
||||
product.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
product.setBoost(5.0F);
|
||||
doc.add(product);
|
||||
|
||||
Field title = new Field(Fields.TITLE, entry.getTitle(), Field.Store.NO, Field.Index.ANALYZED);
|
||||
title.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
//title.setBoost(1.0F);
|
||||
doc.add(title);
|
||||
|
||||
//TODO revision should likely be its own field
|
||||
if (entry.getVersion() != null) {
|
||||
Field version = null;
|
||||
if (entry.getRevision() != null) {
|
||||
version = new Field(Fields.VERSION, entry.getVersion() + " "
|
||||
+ entry.getRevision(), Field.Store.NO, Field.Index.ANALYZED);
|
||||
} else {
|
||||
version = new Field(Fields.VERSION, entry.getVersion(),
|
||||
Field.Store.NO, Field.Index.ANALYZED);
|
||||
}
|
||||
version.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
version.setBoost(0.8F);
|
||||
doc.add(version);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.codesecure.dependencycheck.data.cpe</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes for working with the CPE Lucene Index.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
@@ -0,0 +1,350 @@
|
||||
package org.codesecure.dependencycheck.data.cpe.xml;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import org.codesecure.dependencycheck.data.cpe.Entry;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* A SAX Handler that will parse the CPE XML Listing.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class CPEHandler extends DefaultHandler {
|
||||
|
||||
private static final String CURRENT_SCHEMA_VERSION = "2.2";
|
||||
EntrySaveDelegate saveDelegate = null;
|
||||
Entry entry = null;
|
||||
boolean languageIsUS = false;
|
||||
StringBuilder nodeText = null;
|
||||
boolean skip = false;
|
||||
Element current = new Element();
|
||||
|
||||
/**
|
||||
* Register a EntrySaveDelegate object. When the last node of an entry is
|
||||
* reached if a save delegate has been regsitered the save method will be invoked.
|
||||
*
|
||||
* @param delegate the delegate used to save an entry
|
||||
*/
|
||||
public void registerSaveDelegate(EntrySaveDelegate delegate) {
|
||||
this.saveDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
nodeText = null;
|
||||
current.setNode(qName);
|
||||
if (current.isCpeItemNode()) {
|
||||
entry = new Entry();
|
||||
String temp = attributes.getValue("deprecated");
|
||||
String name = attributes.getValue("name");
|
||||
skip = (temp != null && temp.equals("true"));
|
||||
try {
|
||||
if (!skip && name.startsWith("cpe:/a:")) {
|
||||
entry.setName(name);
|
||||
} else {
|
||||
skip = true;
|
||||
}
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new SAXException(ex);
|
||||
}
|
||||
} else if (current.isTitleNode()) {
|
||||
nodeText = new StringBuilder(100);
|
||||
if ("en-US".equalsIgnoreCase(attributes.getValue("xml:lang"))) {
|
||||
languageIsUS = true;
|
||||
} else {
|
||||
languageIsUS = false;
|
||||
}
|
||||
} else if (current.isMetaNode()) {
|
||||
try {
|
||||
entry.setModificationDate(attributes.getValue("modification-date"));
|
||||
} catch (ParseException ex) {
|
||||
Logger.getLogger(CPEHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
entry.setStatus(attributes.getValue("status"));
|
||||
entry.setNvdId(attributes.getValue("nvd-id"));
|
||||
} else if (current.isSchemaVersionNode()) {
|
||||
nodeText = new StringBuilder(3);
|
||||
} else if (current.isTimestampNode()) {
|
||||
nodeText = new StringBuilder(24);
|
||||
}
|
||||
// } else if (current.isCpeListNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isNotesNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isNoteNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isCheckNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isGeneratorNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isProductNameNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isProductVersionNode()) {
|
||||
// //do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
//nodeText += new String(ch, start, length);
|
||||
if (nodeText != null) {
|
||||
nodeText.append(ch, start, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
current.setNode(qName);
|
||||
if (current.isCpeItemNode()) {
|
||||
if (saveDelegate != null && !skip) {
|
||||
try {
|
||||
saveDelegate.saveEntry(entry);
|
||||
} catch (CorruptIndexException ex) {
|
||||
Logger.getLogger(CPEHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new SAXException(ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CPEHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new SAXException(ex);
|
||||
}
|
||||
entry = null;
|
||||
}
|
||||
} else if (current.isTitleNode()) {
|
||||
if (languageIsUS) {
|
||||
entry.setTitle(nodeText.toString());
|
||||
}
|
||||
} else if (current.isSchemaVersionNode() && !CURRENT_SCHEMA_VERSION.equals(nodeText.toString())) {
|
||||
throw new SAXException("ERROR: Invalid Schema Version, expected: "
|
||||
+ CURRENT_SCHEMA_VERSION + ", file is: " + nodeText);
|
||||
}
|
||||
// } else if (current.isCpeListNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isMetaNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isNotesNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isNoteNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isCheckNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isGeneratorNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isProductNameNode()) {
|
||||
// //do nothing
|
||||
// } else if (current.isProductVersionNode()) {
|
||||
// //do nothing
|
||||
// else if (current.isTimestampNode()) {
|
||||
// //do nothing
|
||||
// } else {
|
||||
// throw new SAXException("ERROR STATE: Unexpected qName '" + qName + "'");
|
||||
// }
|
||||
}
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
|
||||
/**
|
||||
* A simple class to maintain information about the current element while parsing
|
||||
* the CPE XML.
|
||||
*/
|
||||
protected class Element {
|
||||
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String CPE_LIST = "cpe-list";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String CPE_ITEM = "cpe-item";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String TITLE = "title";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String NOTES = "notes";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String NOTE = "note";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String CHECK = "check";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String META = "meta:item-metadata";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String GENERATOR = "generator";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String PRODUCT_NAME = "product_name";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String PRODUCT_VERSION = "product_version";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String SCHEMA_VERSION = "schema_version";
|
||||
/**
|
||||
* A node type in the CPE Schema 2.2
|
||||
*/
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
private String node = null;
|
||||
|
||||
/**
|
||||
* Gets the value of node
|
||||
*
|
||||
* @return the value of node
|
||||
*/
|
||||
public String getNode() {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of node
|
||||
*
|
||||
* @param node new value of node
|
||||
*/
|
||||
public void setNode(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the CPE_LIST node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCpeListNode() {
|
||||
return CPE_LIST.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the CPE_ITEM node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCpeItemNode() {
|
||||
return CPE_ITEM.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the TITLE node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isTitleNode() {
|
||||
return TITLE.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the NOTES node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isNotesNode() {
|
||||
return NOTES.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the NOTE node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isNoteNode() {
|
||||
return NOTE.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the CHECK node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCheckNode() {
|
||||
return CHECK.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the META node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isMetaNode() {
|
||||
return META.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the GENERATOR node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isGeneratorNode() {
|
||||
return GENERATOR.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the PRODUCT_NAME node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isProductNameNode() {
|
||||
return PRODUCT_NAME.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the PRODUCT_VERSION node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isProductVersionNode() {
|
||||
return PRODUCT_VERSION.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the SCHEMA_VERSION node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isSchemaVersionNode() {
|
||||
return SCHEMA_VERSION.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the TIMESTAMP node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isTimestampNode() {
|
||||
return TIMESTAMP.equals(node);
|
||||
}
|
||||
}
|
||||
// </editor-fold>
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.codesecure.dependencycheck.data.cpe.xml;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import org.codesecure.dependencycheck.data.cpe.Entry;
|
||||
import java.io.IOException;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
|
||||
/**
|
||||
*
|
||||
* An interface used to define the save function used when parsing the CPE XML
|
||||
* file.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public interface EntrySaveDelegate {
|
||||
|
||||
/**
|
||||
* Saves a CPE Entry into the Lucene index.
|
||||
*
|
||||
* @param entry a CPE entry.
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
void saveEntry(Entry entry) throws CorruptIndexException, IOException;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.codesecure.dependencycheck.data.cpe.xml;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import org.codesecure.dependencycheck.data.cpe.Indexer;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Imports a CPE XML file into the Lucene CPE Index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Importer {
|
||||
|
||||
/**
|
||||
* Private constructor for utility class.
|
||||
*/
|
||||
private Importer() {
|
||||
|
||||
}
|
||||
/**
|
||||
* Imports the CPE XML File into the Lucene Index.
|
||||
*
|
||||
* @param path the path to the CPE XML file.
|
||||
* @throws ParserConfigurationException is thrown if the parser is misconfigured.
|
||||
* @throws SAXException is thrown when there is a SAXException.
|
||||
* @throws IOException is thrown when there is an IOException.
|
||||
*/
|
||||
public static void importXML(String path) throws ParserConfigurationException, SAXException, IOException {
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
SAXParser saxParser = factory.newSAXParser();
|
||||
CPEHandler handler = new CPEHandler();
|
||||
Indexer indexer = new Indexer();
|
||||
indexer.open();
|
||||
handler.registerSaveDelegate(indexer);
|
||||
File f = new File(path);
|
||||
saxParser.parse(f, handler);
|
||||
indexer.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.codesecure.dependencycheck.data.cpe.xml</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes used to parse the CPE XML file.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.codesecure.dependencycheck.data.cpe.xml;
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.codesecure.dependencycheck.data</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains utility classes used to work with the Lucene Indexes.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.codesecure.dependencycheck.data;
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.codesecure.dependencycheck</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Includes the main entry point for the DependencyChecker.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.codesecure.dependencycheck;
|
||||
@@ -0,0 +1,124 @@
|
||||
package org.codesecure.dependencycheck.reporting;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.context.Context;
|
||||
import org.apache.velocity.runtime.RuntimeConstants;
|
||||
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
|
||||
import org.apache.velocity.tools.ToolManager;
|
||||
import org.apache.velocity.tools.config.EasyFactoryConfiguration;
|
||||
import org.codesecure.dependencycheck.scanner.Dependency;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class ReportGenerator {
|
||||
|
||||
|
||||
public void generateReports(String outputDir, String applicationName, List<Dependency> dependencies) throws IOException, Exception {
|
||||
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
properties.put("dependencies",dependencies);
|
||||
properties.put("applicationName", applicationName);
|
||||
|
||||
String reportName = applicationName.replaceAll("[^a-zA-Z0-9-_ \\.]+", "");
|
||||
String filename = outputDir + File.separatorChar + reportName;
|
||||
generateReport("HtmlReport",filename+".html",properties);
|
||||
//generateReport("XmlReport",filename+".xml",properties);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* much of this code is from http://stackoverflow.com/questions/2931516/loading-velocity-template-inside-a-jar-file
|
||||
* @param templateName the name of the template to load.
|
||||
* @param outFileName The filename and path to write the report to.
|
||||
* @param properties a map of properties to load into the velocity context.
|
||||
* @throws IOException is thrown when the template file does not exist.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
protected void generateReport(String templateName, String outFileName,
|
||||
Map<String, Object> properties) throws IOException, Exception {
|
||||
|
||||
VelocityEngine ve = new VelocityEngine();
|
||||
ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
|
||||
ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
|
||||
|
||||
ToolManager manager = new ToolManager();
|
||||
Context context = manager.createContext();
|
||||
EasyFactoryConfiguration config = new EasyFactoryConfiguration();
|
||||
config.addDefaultTools();
|
||||
config.toolbox("application")
|
||||
.tool("esc","org.apache.velocity.tools.generic.EscapeTool")
|
||||
.tool("org.apache.velocity.tools.generic.DateTool");
|
||||
|
||||
manager.configure(config);
|
||||
|
||||
ve.init();
|
||||
|
||||
final String templatePath = "templates/" + templateName + ".vsl";
|
||||
InputStream input = this.getClass().getClassLoader().getResourceAsStream(templatePath);
|
||||
if (input == null) {
|
||||
throw new IOException("Template file doesn't exist");
|
||||
}
|
||||
|
||||
InputStreamReader reader = new InputStreamReader(input);
|
||||
BufferedWriter writer = null;
|
||||
|
||||
//VelocityContext context = new VelocityContext();
|
||||
|
||||
//load the data into the context
|
||||
if (properties != null) {
|
||||
for (Map.Entry<String, Object> property : properties.entrySet()) {
|
||||
context.put(property.getKey(), property.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(new File(outFileName)));
|
||||
|
||||
if (!ve.evaluate(context, writer, templatePath, reader)) {
|
||||
throw new Exception("Failed to convert the template into html.");
|
||||
}
|
||||
writer.flush();
|
||||
} finally {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
try {
|
||||
reader.close();
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An interface that defines an Analyzer that is used to identify Dependencies.
|
||||
* An analyzer will collect information about the dependency in the form of
|
||||
* Evidence.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public interface Analyzer {
|
||||
|
||||
/**
|
||||
* Loads a specified file and collects data needed to determine any associated CPE/CVE entries.
|
||||
*
|
||||
* @param file path to the file.
|
||||
* @return Dependency the dependency containing evidence needed to determine associated CPE/CVE entries.
|
||||
* @throws IOException is thrown if there is an error reading the dependency file
|
||||
*/
|
||||
Dependency insepct(File file) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A program dependency. This object is one of the core components within
|
||||
* DependencyCheck. It is used to collect information about the dependency
|
||||
* in the form of evidence. The Evidence is then used to determine if there
|
||||
* are any known, published, vulnerabilities associated with the program
|
||||
* dependency.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Dependency {
|
||||
|
||||
/**
|
||||
* The file path of the dependency.
|
||||
*/
|
||||
private String filePath = null;
|
||||
/**
|
||||
* The file name of the dependency.
|
||||
*/
|
||||
private String fileName = null;
|
||||
/**
|
||||
* The md5 hash of the dependency.
|
||||
*/
|
||||
private String md5sum = null;
|
||||
/**
|
||||
* The SHA1 hash of the dependency.
|
||||
*/
|
||||
private String sha1sum = null;
|
||||
/**
|
||||
* A list of CPEs.
|
||||
*/
|
||||
private List<String> cpes = null;
|
||||
/**
|
||||
* A collection of vendor evidence.
|
||||
*/
|
||||
protected EvidenceCollection vendorEvidence = null;
|
||||
/**
|
||||
* A collection of title evidence.
|
||||
*/
|
||||
protected EvidenceCollection titleEvidence = null;
|
||||
/**
|
||||
* A collection of version evidence.
|
||||
*/
|
||||
protected EvidenceCollection versionEvidence = null;
|
||||
|
||||
public Dependency() {
|
||||
vendorEvidence = new EvidenceCollection();
|
||||
titleEvidence = new EvidenceCollection();
|
||||
versionEvidence = new EvidenceCollection();
|
||||
cpes = new ArrayList<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name of the JAR.
|
||||
*
|
||||
* @return the file name of the JAR
|
||||
*/
|
||||
public String getFileName() {
|
||||
return this.fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file name of the JAR.
|
||||
*
|
||||
* @param fileName the file name of the JAR
|
||||
*/
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file path of the JAR.
|
||||
* @param filePath the file path of the JAR
|
||||
*/
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file path of the JAR.
|
||||
* @return the file path of the JAR.
|
||||
*/
|
||||
public String getFilePath() {
|
||||
return this.filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MD5 Checksum of the JAR file.
|
||||
*
|
||||
* @return the MD5 Checksum
|
||||
*/
|
||||
public String getMd5sum() {
|
||||
return this.md5sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the MD5 Checksum of the JAR.
|
||||
*
|
||||
* @param md5sum the MD5 Checksum
|
||||
*/
|
||||
public void setMd5sum(String md5sum) {
|
||||
this.md5sum = md5sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SHA1 Checksum of the JAR.
|
||||
*
|
||||
* @return the SHA1 Checksum
|
||||
*/
|
||||
public String getSha1sum() {
|
||||
return this.sha1sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the SHA1 Checksum of the JAR.
|
||||
*
|
||||
* @param sha1sum the SHA1 Checksum
|
||||
*/
|
||||
public void setSha1sum(String sha1sum) {
|
||||
this.sha1sum = sha1sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a List of possible CPE keys.
|
||||
*
|
||||
* @return an ArrayList containing possible CPE keys.
|
||||
*/
|
||||
public List<String> getCPEs() {
|
||||
return this.cpes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a List of possible CPE keys.
|
||||
*
|
||||
* @param cpe A list of CPE values.
|
||||
*/
|
||||
public void setCPEs(List<String> cpe) {
|
||||
this.cpes = cpe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry to the list of detected CPE keys for the dependency file.
|
||||
*
|
||||
* @param cpe a CPE key for the dependency file
|
||||
*/
|
||||
public void addCPEentry(String cpe) {
|
||||
if (cpes == null) {
|
||||
cpes = new ArrayList<String>();
|
||||
}
|
||||
this.cpes.add(cpe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the evidence used to identify this dependency.
|
||||
*
|
||||
* @return an EvidenceCollection.
|
||||
*/
|
||||
public EvidenceCollection getEvidence() {
|
||||
return EvidenceCollection.mergeUsed(this.titleEvidence, this.vendorEvidence, this.versionEvidence);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the evidence used to identify this dependency.
|
||||
*
|
||||
* @return an EvidenceCollection.
|
||||
*/
|
||||
public EvidenceCollection getEvidenceUsed() {
|
||||
EvidenceCollection ec = EvidenceCollection.mergeUsed(this.titleEvidence, this.vendorEvidence, this.versionEvidence);
|
||||
return ec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Vendor Evidence.
|
||||
*
|
||||
* @return an EvidenceCollection.
|
||||
*/
|
||||
public EvidenceCollection getVendorEvidence() {
|
||||
return this.vendorEvidence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Title Evidence.
|
||||
*
|
||||
* @return an EvidenceCollection.
|
||||
*/
|
||||
public EvidenceCollection getTitleEvidence() {
|
||||
return this.titleEvidence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Version Evidence.
|
||||
*
|
||||
* @return an EvidenceCollection.
|
||||
*/
|
||||
public EvidenceCollection getVersionEvidence() {
|
||||
return this.versionEvidence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the specified string was used when searching.
|
||||
*
|
||||
* @param str is the string that is being checked if it was used.
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean containsUsedString(String str) {
|
||||
if (str == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String fnd = str.toLowerCase();
|
||||
|
||||
//TODO add the filename is analyzed and added as evidence
|
||||
//TODO remove special characters from filename and check this (including spaces)
|
||||
if (this.fileName != null && this.fileName.contains(fnd)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vendorEvidence.containsUsedString(str)) {
|
||||
return true;
|
||||
}
|
||||
if (titleEvidence.containsUsedString(str)) {
|
||||
return true;
|
||||
}
|
||||
if (versionEvidence.containsUsedString(fnd)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Evidence is a piece of information about a Dependency.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Evidence {
|
||||
|
||||
/**
|
||||
* The confidence that the evidence is "high" quality.
|
||||
*/
|
||||
public enum Confidence {
|
||||
/**
|
||||
* High confidence evidence.
|
||||
*/
|
||||
HIGH,
|
||||
/**
|
||||
* Medium confidence evidence.
|
||||
*/
|
||||
MEDIUM,
|
||||
/**
|
||||
* Low confidence evidence.
|
||||
*/
|
||||
LOW
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Evidence object.
|
||||
*/
|
||||
public Evidence() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Evidence objects.
|
||||
*
|
||||
* @param source the source of the evidence.
|
||||
* @param name the name of the evidence.
|
||||
* @param value the value of the evidence.
|
||||
* @param confidence the confidence of the evidence.
|
||||
*/
|
||||
public Evidence(String source, String name, String value, Confidence confidence) {
|
||||
this.source = source;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.confidence = confidence;
|
||||
}
|
||||
/**
|
||||
* The name of the evidence.
|
||||
*/
|
||||
protected String name;
|
||||
|
||||
/**
|
||||
* Get the value of name
|
||||
*
|
||||
* @return the value of name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of name
|
||||
*
|
||||
* @param name new value of name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* The source of the evidence.
|
||||
*/
|
||||
protected String source;
|
||||
|
||||
/**
|
||||
* Get the value of source
|
||||
*
|
||||
* @return the value of source
|
||||
*/
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of source
|
||||
*
|
||||
* @param source new value of source
|
||||
*/
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
/**
|
||||
* The value of the evidence.
|
||||
*/
|
||||
protected String value;
|
||||
|
||||
/**
|
||||
* Get the value of value
|
||||
*
|
||||
* @return the value of value
|
||||
*/
|
||||
public String getValue() {
|
||||
used = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of value
|
||||
*
|
||||
* @param value new value of value
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
/**
|
||||
* A value indicating if the Evidence has been "used" (aka read).
|
||||
*/
|
||||
protected boolean used;
|
||||
|
||||
/**
|
||||
* Get the value of used
|
||||
*
|
||||
* @return the value of used
|
||||
*/
|
||||
public boolean isUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of used
|
||||
*
|
||||
* @param used new value of used
|
||||
*/
|
||||
public void setUsed(boolean used) {
|
||||
this.used = used;
|
||||
}
|
||||
/**
|
||||
* The confidence level for the evidence.
|
||||
*/
|
||||
protected Confidence confidence;
|
||||
|
||||
/**
|
||||
* Get the value of confidence
|
||||
*
|
||||
* @return the value of confidence
|
||||
*/
|
||||
public Confidence getConfidence() {
|
||||
return confidence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of confidence
|
||||
*
|
||||
* @param confidence new value of confidence
|
||||
*/
|
||||
public void setConfidence(Confidence confidence) {
|
||||
this.confidence = confidence;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.codesecure.dependencycheck.utils.Filter;
|
||||
|
||||
/**
|
||||
* Used to maintain a collection of Evidence.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class EvidenceCollection implements Iterable<Evidence> {
|
||||
|
||||
/**
|
||||
* Used to iterate over high confidence evidence contained in the collection.
|
||||
*/
|
||||
private static final Filter<Evidence> HIGH_CONFIDENCE =
|
||||
new Filter<Evidence>() {
|
||||
|
||||
public boolean passes(Evidence evidence) {
|
||||
return evidence.getConfidence() == Evidence.Confidence.HIGH;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Used to iterate over medium confidence evidence contained in the collection.
|
||||
*/
|
||||
private static final Filter<Evidence> MEDIUM_CONFIDENCE =
|
||||
new Filter<Evidence>() {
|
||||
|
||||
public boolean passes(Evidence evidence) {
|
||||
return evidence.getConfidence() == Evidence.Confidence.MEDIUM;
|
||||
}
|
||||
};
|
||||
/*
|
||||
* Used to iterate over low confidence evidence contained in the collection.
|
||||
*/
|
||||
private static final Filter<Evidence> LOW_CONFIDENCE =
|
||||
new Filter<Evidence>() {
|
||||
|
||||
public boolean passes(Evidence evidence) {
|
||||
return evidence.getConfidence() == Evidence.Confidence.LOW;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Used to iterate over evidence that has was used (aka read) from the collection.
|
||||
*/
|
||||
private static final Filter<Evidence> EVIDENCE_USED =
|
||||
new Filter<Evidence>() {
|
||||
|
||||
public boolean passes(Evidence evidence) {
|
||||
return evidence.isUsed();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to iterate over evidence of the specified confidence.
|
||||
* @param confidence the confidence level for the evidence to be iterated over.
|
||||
* @return Iterable<Evidence>.
|
||||
*/
|
||||
public final Iterable<Evidence> iterator(Evidence.Confidence confidence) {
|
||||
if (confidence == Evidence.Confidence.HIGH) {
|
||||
return EvidenceCollection.HIGH_CONFIDENCE.filter(this.list);
|
||||
} else if (confidence == Evidence.Confidence.MEDIUM) {
|
||||
return EvidenceCollection.MEDIUM_CONFIDENCE.filter(this.list);
|
||||
} else {
|
||||
return EvidenceCollection.LOW_CONFIDENCE.filter(this.list);
|
||||
}
|
||||
}
|
||||
private List<Evidence> list = null;
|
||||
private List<String> weightedStrings = null;
|
||||
|
||||
/**
|
||||
* Creates a new EvidenceCollection.
|
||||
*/
|
||||
public EvidenceCollection() {
|
||||
list = new ArrayList<Evidence>();
|
||||
weightedStrings = new ArrayList<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds evidence to the collection.
|
||||
* @param e Evidence.
|
||||
*/
|
||||
public void addEvidence(Evidence e) {
|
||||
list.add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Evidence object from the parameters and adds the resulting
|
||||
* object to the collection.
|
||||
*
|
||||
* @param source the source of the Evidence.
|
||||
* @param name the name of the Evidence.
|
||||
* @param value the value of the Evidence.
|
||||
* @param confidence the confidence of the Evidence.
|
||||
*/
|
||||
public void addEvidence(String source, String name, String value, Evidence.Confidence confidence) {
|
||||
Evidence e = new Evidence(source, name, value, confidence);
|
||||
addEvidence(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds term to the weighting collection. The terms added here are used later
|
||||
* to boost the score of other terms. This is a way of combining evidence from
|
||||
* multiple sources to boost the confidence of the given evidence.
|
||||
*
|
||||
* Example: The term 'Apache' is found in the manifest of a JAR and is added to the
|
||||
* Collection. When we parse the package names within the JAR file we may add
|
||||
* these package names to the "weighted" strings collection to boost the score
|
||||
* in the Lucene query. That way when we construct the Lucene query we find the
|
||||
* term Apache in the collection AND in the weighted strings; as such, we will
|
||||
* boost the confidence of the term Apache.
|
||||
*
|
||||
* @param str to add to the weighting collection.
|
||||
*/
|
||||
public void addWeighting(String str) {
|
||||
if (!weightedStrings.contains(str)) {
|
||||
weightedStrings.add(str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of Weightings - a list of terms that are believed to be of
|
||||
* higher confidence when also found in another location.
|
||||
*
|
||||
* @return List<String>
|
||||
*/
|
||||
public List<String> getWeighting() {
|
||||
return weightedStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the iterator interface for the Evidence Collection.
|
||||
* @return an Iterator<Evidence>.
|
||||
*/
|
||||
public Iterator<Evidence> iterator() {
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if a given string was used (aka read).
|
||||
* @param text the string to search for.
|
||||
* @return whether or not the string was used.
|
||||
*/
|
||||
public boolean containsUsedString(String text) {
|
||||
if (text == null) {
|
||||
return false;
|
||||
}
|
||||
text = text.toLowerCase();
|
||||
|
||||
for (Evidence e : this.list) {
|
||||
if (e.used && e.value.contains(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the collection contains evidence of a specified
|
||||
* Confidence.
|
||||
* @param confidence A Confidence value.
|
||||
* @return boolean.
|
||||
*/
|
||||
public boolean contains(Evidence.Confidence confidence) {
|
||||
for (Evidence e : list) {
|
||||
if (e.confidence == confidence) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges multiple EvidenceCollections together, only merging evidence that
|
||||
* was used, into a new EvidenceCollection.
|
||||
*
|
||||
* @param ec One or more EvidenceCollections.
|
||||
* @return a new EvidenceCollection containing the used evidence.
|
||||
*/
|
||||
public static EvidenceCollection mergeUsed(EvidenceCollection... ec) {
|
||||
EvidenceCollection ret = new EvidenceCollection();
|
||||
for (EvidenceCollection col : ec) {
|
||||
for (Evidence e : col.list) {
|
||||
if (e.isUsed()) {
|
||||
ret.addEvidence(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of evidence 'values' for a given confidence.
|
||||
* @param confidence the confidence filter applied to the toString method.
|
||||
* @return a string containing the evidence.
|
||||
*/
|
||||
public String toString(Evidence.Confidence confidence) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Evidence e : this.iterator(confidence)) {
|
||||
sb.append(e.getValue()).append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of evidence 'values'.
|
||||
* @return a string containing the evidence.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Evidence e : this.list) {
|
||||
sb.append(e.getValue()).append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import org.codesecure.dependencycheck.utils.Checksum;
|
||||
|
||||
/**
|
||||
*
|
||||
* Used to load a JAR file and collect information that can be used to determine the associated CPE.
|
||||
*
|
||||
* <!--
|
||||
* ideas - scan the JAR to see if there is a "version" final static string?
|
||||
* scan manifest for version info
|
||||
* get md5 and sh1 checksums to lookup file via maven centrals hosted md5sum files
|
||||
* examine file name itself for version info
|
||||
* -->
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class JarAnalyzer implements Analyzer {
|
||||
|
||||
private static final String BUNDLE_VERSION = "Bundle-Version"; //: 2.1.2
|
||||
private static final String BUNDLE_DESCRIPTION = "Bundle-Description"; //: Apache Struts 2
|
||||
private static final String BUNDLE_NAME = "Bundle-Name"; //: Struts 2 Core
|
||||
private static final String BUNDLE_VENDOR = "Bundle-Vendor"; //: Apache Software Foundation
|
||||
|
||||
|
||||
private enum STRING_STATE {
|
||||
ALPHA,
|
||||
NUMBER,
|
||||
OTHER
|
||||
}
|
||||
private STRING_STATE determineState(char c) {
|
||||
if (c>='0' && c<='9' || c=='.') {
|
||||
return STRING_STATE.NUMBER;
|
||||
} else if (c>='a' && c<='z') {
|
||||
return STRING_STATE.ALPHA;
|
||||
} else {
|
||||
return STRING_STATE.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a specified JAR file and collects information from the manifest and
|
||||
* checksums to identify the correct CPE information.
|
||||
*
|
||||
* @param file path to the JAR file.
|
||||
* @return a dependency derived for the specified file.
|
||||
* @throws IOException is thrown if there is an error reading the JAR file.
|
||||
*/
|
||||
public Dependency insepct(File file) throws IOException {
|
||||
|
||||
Dependency dependency = new Dependency();
|
||||
|
||||
String fileName = file.getName();
|
||||
dependency.setFileName(fileName);
|
||||
dependency.setFilePath(file.getCanonicalPath());
|
||||
String fileNameEvidence = fileName.substring(0,fileName.length()-4)
|
||||
.toLowerCase()
|
||||
.replace('-', ' ')
|
||||
.replace('_', ' ');
|
||||
StringBuilder sb = new StringBuilder(fileNameEvidence.length());
|
||||
STRING_STATE state = determineState(fileNameEvidence.charAt(0));
|
||||
|
||||
for(int i=0;i<fileNameEvidence.length();i++) {
|
||||
char c = fileNameEvidence.charAt(i);
|
||||
STRING_STATE new_state = determineState(c);
|
||||
if (new_state != state) {
|
||||
sb.append(' ');
|
||||
state = new_state;
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
Pattern rx = Pattern.compile("\\s\\s+");
|
||||
fileNameEvidence = rx.matcher(sb.toString()).replaceAll(" ");
|
||||
dependency.getTitleEvidence().addEvidence("jar", "file name",
|
||||
fileNameEvidence, Evidence.Confidence.HIGH);
|
||||
dependency.getVendorEvidence().addEvidence("jar", "file name",
|
||||
fileNameEvidence, Evidence.Confidence.HIGH);
|
||||
dependency.getVersionEvidence().addEvidence("jar", "file name",
|
||||
fileNameEvidence, Evidence.Confidence.HIGH);
|
||||
|
||||
String md5 = null;
|
||||
String sha1 = null;
|
||||
try {
|
||||
md5 = Checksum.getMD5Checksum(file);
|
||||
sha1 = Checksum.getSHA1Checksum(file);
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
dependency.setMd5sum(md5);
|
||||
dependency.setSha1sum(sha1);
|
||||
|
||||
parseManifest(dependency);
|
||||
analyzePackageNames(dependency);
|
||||
|
||||
//TODO - can we get "version" information from the filename? add it as medium confidence?
|
||||
// strip extension. find first numeric, chop off the first part. consider replacing [_-] with .
|
||||
//dependency.getVersionEvidence().addEvidence("jar", "file name",
|
||||
// version from file, Evidence.Confidence.MEDIUM);
|
||||
|
||||
return dependency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes the path information of the classes contained within the JarAnalyzer
|
||||
* to try and determine possible vendor or product names. If any are found they are
|
||||
* stored in the packageVendor and packageProduct hashSets.
|
||||
*
|
||||
* @param dependency A reference to the dependency.
|
||||
* @throws IOException is thrown if there is an error reading the JAR file.
|
||||
*/
|
||||
protected void analyzePackageNames(Dependency dependency) throws IOException {
|
||||
|
||||
JarFile jar = new JarFile(dependency.getFilePath());
|
||||
java.util.Enumeration en = jar.entries();
|
||||
|
||||
HashMap<String, Integer> level0 = new HashMap<String, Integer>();
|
||||
HashMap<String, Integer> level1 = new HashMap<String, Integer>();
|
||||
HashMap<String, Integer> level2 = new HashMap<String, Integer>();
|
||||
HashMap<String, Integer> level3 = new HashMap<String, Integer>();
|
||||
int count = 0;
|
||||
while (en.hasMoreElements()) {
|
||||
java.util.jar.JarEntry entry = (java.util.jar.JarEntry) en.nextElement();
|
||||
if (entry.getName().endsWith(".class") && entry.getName().contains("/")) {
|
||||
String[] path = entry.getName().toLowerCase().split("/");
|
||||
|
||||
if ("java".equals(path[0])
|
||||
|| "javax".equals(path[0])
|
||||
|| ("com".equals(path[0]) && "sun".equals(path[0]))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
count += 1;
|
||||
String temp = path[0];
|
||||
if (level0.containsKey(temp)) {
|
||||
level0.put(temp, level0.get(temp) + 1);
|
||||
} else {
|
||||
level0.put(temp, 1);
|
||||
}
|
||||
|
||||
if (path.length > 2) {
|
||||
temp += "/" + path[1];
|
||||
if (level1.containsKey(temp)) {
|
||||
level1.put(temp, level1.get(temp) + 1);
|
||||
} else {
|
||||
level1.put(temp, 1);
|
||||
}
|
||||
}
|
||||
if (path.length > 3) {
|
||||
temp += "/" + path[2];
|
||||
if (level2.containsKey(temp)) {
|
||||
level2.put(temp, level2.get(temp) + 1);
|
||||
} else {
|
||||
level2.put(temp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (path.length > 4) {
|
||||
temp += "/" + path[3];
|
||||
if (level3.containsKey(temp)) {
|
||||
level3.put(temp, level3.get(temp) + 1);
|
||||
} else {
|
||||
level3.put(temp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
EvidenceCollection vendor = dependency.getVendorEvidence();
|
||||
EvidenceCollection title = dependency.getTitleEvidence();
|
||||
|
||||
for (String s : level0.keySet()) {
|
||||
if (!"org".equals(s) && !"com".equals(s)) {
|
||||
vendor.addWeighting(s);
|
||||
title.addWeighting(s);
|
||||
vendor.addEvidence("jar", "package", s, Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", s, Evidence.Confidence.LOW);
|
||||
}
|
||||
}
|
||||
for (String s : level1.keySet()) {
|
||||
float ratio = level1.get(s);
|
||||
ratio /= count;
|
||||
if (ratio > 0.5) {
|
||||
String[] parts = s.split("/");
|
||||
if ("org".equals(parts[0]) || "com".equals(parts[0])) {
|
||||
vendor.addWeighting(parts[1]);
|
||||
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
} else {
|
||||
vendor.addWeighting(parts[0]);
|
||||
title.addWeighting(parts[1]);
|
||||
vendor.addEvidence("jar", "package", parts[0], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String s : level2.keySet()) {
|
||||
float ratio = level2.get(s);
|
||||
ratio /= count;
|
||||
if (ratio > 0.4) {
|
||||
String[] parts = s.split("/");
|
||||
if ("org".equals(parts[0]) || "com".equals(parts[0])) {
|
||||
vendor.addWeighting(parts[1]);
|
||||
title.addWeighting(parts[2]);
|
||||
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
|
||||
} else {
|
||||
vendor.addWeighting(parts[0]);
|
||||
vendor.addWeighting(parts[1]);
|
||||
title.addWeighting(parts[1]);
|
||||
title.addWeighting(parts[2]);
|
||||
vendor.addEvidence("jar", "package", parts[0], Evidence.Confidence.LOW);
|
||||
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String s : level3.keySet()) {
|
||||
float ratio = level3.get(s);
|
||||
ratio /= count;
|
||||
if (ratio > 0.3) {
|
||||
String[] parts = s.split("/");
|
||||
if ("org".equals(parts[0]) || "com".equals(parts[0])) {
|
||||
vendor.addWeighting(parts[1]);
|
||||
vendor.addWeighting(parts[2]);
|
||||
title.addWeighting(parts[2]);
|
||||
title.addWeighting(parts[3]);
|
||||
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
vendor.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[3], Evidence.Confidence.LOW);
|
||||
|
||||
} else {
|
||||
vendor.addWeighting(parts[0]);
|
||||
vendor.addWeighting(parts[1]);
|
||||
vendor.addWeighting(parts[2]);
|
||||
title.addWeighting(parts[1]);
|
||||
title.addWeighting(parts[2]);
|
||||
title.addWeighting(parts[3]);
|
||||
vendor.addEvidence("jar", "package", parts[0], Evidence.Confidence.LOW);
|
||||
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
vendor.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
|
||||
title.addEvidence("jar", "package", parts[3], Evidence.Confidence.LOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the manifest from the JAR file and collects the:</p>
|
||||
* <ul><li>Implementation Title</li>
|
||||
* <li>Implementation Version</li>
|
||||
* <li>Implementation Vendor</li>
|
||||
* <li>Implementation VendorId</li>
|
||||
* <li>Bundle Name</li>
|
||||
* <li>Bundle Version</li>
|
||||
* <li>Bundle Vendor</li>
|
||||
* <li>Bundle Description</li>
|
||||
* <li>Main Class</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param dependency A reference to the dependency.
|
||||
* @throws IOException if there is an issue reading the JAR file.
|
||||
*/
|
||||
protected void parseManifest(Dependency dependency) throws IOException {
|
||||
JarFile jar = new JarFile(dependency.getFilePath());
|
||||
Manifest manifest = jar.getManifest();
|
||||
Attributes atts = manifest.getMainAttributes();
|
||||
|
||||
EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
|
||||
EvidenceCollection titleEvidence = dependency.getTitleEvidence();
|
||||
EvidenceCollection versionEvidence = dependency.getVendorEvidence();
|
||||
|
||||
String source = "Manifest";
|
||||
String name = Attributes.Name.IMPLEMENTATION_TITLE.toString();
|
||||
String value = atts.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
|
||||
if (value != null) {
|
||||
titleEvidence.addEvidence(source, name, value, Evidence.Confidence.HIGH);
|
||||
}
|
||||
|
||||
name = Attributes.Name.IMPLEMENTATION_VERSION.toString();
|
||||
value = atts.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||
if (value != null) {
|
||||
versionEvidence.addEvidence(source, name, value, Evidence.Confidence.HIGH);
|
||||
}
|
||||
|
||||
name = Attributes.Name.IMPLEMENTATION_VENDOR.toString();
|
||||
value = atts.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
|
||||
if (value != null) {
|
||||
vendorEvidence.addEvidence(source, name, value, Evidence.Confidence.HIGH);
|
||||
}
|
||||
|
||||
name = Attributes.Name.IMPLEMENTATION_VENDOR_ID.toString();
|
||||
value = atts.getValue(Attributes.Name.IMPLEMENTATION_VENDOR_ID);
|
||||
if (value != null) {
|
||||
vendorEvidence.addEvidence(source, name, value, Evidence.Confidence.MEDIUM);
|
||||
}
|
||||
|
||||
name = BUNDLE_DESCRIPTION;
|
||||
value = atts.getValue(BUNDLE_DESCRIPTION);
|
||||
if (value != null) {
|
||||
titleEvidence.addEvidence(source, name, value, Evidence.Confidence.MEDIUM);
|
||||
}
|
||||
|
||||
name = BUNDLE_VENDOR;
|
||||
value = atts.getValue(BUNDLE_VENDOR);
|
||||
if (value != null) {
|
||||
vendorEvidence.addEvidence(source, name, value, Evidence.Confidence.MEDIUM);
|
||||
}
|
||||
|
||||
name = BUNDLE_VERSION;
|
||||
value = atts.getValue(BUNDLE_VERSION);
|
||||
if (value != null) {
|
||||
versionEvidence.addEvidence(source, name, value, Evidence.Confidence.MEDIUM);
|
||||
}
|
||||
name = BUNDLE_NAME;
|
||||
value = atts.getValue(BUNDLE_NAME);
|
||||
if (value != null) {
|
||||
titleEvidence.addEvidence(source, name, value, Evidence.Confidence.LOW);
|
||||
}
|
||||
|
||||
name = Attributes.Name.MAIN_CLASS.toString();
|
||||
value = atts.getValue(Attributes.Name.MAIN_CLASS);
|
||||
if (value != null) {
|
||||
titleEvidence.addEvidence(source, name, value, Evidence.Confidence.MEDIUM);
|
||||
vendorEvidence.addEvidence(source, name, value, Evidence.Confidence.MEDIUM);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.codesecure.dependencycheck.utils.Settings;
|
||||
import org.codesecure.dependencycheck.utils.Settings.KEYS;
|
||||
|
||||
/**
|
||||
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and
|
||||
* used to process the files found by the scanner, if a file is encountered and
|
||||
* an Analyzer is associated with the file type then the file is turned into a
|
||||
* dependency by the Analyzer.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Scanner {
|
||||
|
||||
/**
|
||||
* The list of dependencies.
|
||||
*/
|
||||
protected List<Dependency> dependencies = new ArrayList<Dependency>();
|
||||
/**
|
||||
* A Map of analyzers - the key is the file extension.
|
||||
*/
|
||||
protected Map<String, Analyzer> analyzers = new HashMap<String, Analyzer>();
|
||||
|
||||
/**
|
||||
* Creates a new Scanner.
|
||||
*/
|
||||
public Scanner() {
|
||||
loadAnalyzers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the analyzers specified in the configuration file (or system properties).
|
||||
*/
|
||||
private void loadAnalyzers() {
|
||||
Map<String, String> associations = Settings.getPropertiesByPrefix(KEYS.FILE_EXTENSION_ANALYZER_ASSOCIATION_PREFIX);
|
||||
for (Map.Entry<String, String> entry : associations.entrySet()) {
|
||||
addAnalyzer(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Analyzer to the collection of analyzers and associates the
|
||||
* analyzer with a file extension.
|
||||
*
|
||||
* If the specified class does not implement 'org.codesecure.dependencycheck.detect.Analyzer'
|
||||
* the load will fail mostly silently - only writting the failure to the log file.
|
||||
*
|
||||
* @param extension the file extension that this analyzer can analyze.
|
||||
* @param className the fully qualified classname of the Analyzer.
|
||||
*/
|
||||
public final void addAnalyzer(String extension, String className) {
|
||||
|
||||
ClassLoader loader = this.getClass().getClassLoader();
|
||||
try {
|
||||
Class analyzer = loader.loadClass(className);
|
||||
boolean implmnts = false;
|
||||
for (Class p : analyzer.getInterfaces()) {
|
||||
if (org.codesecure.dependencycheck.scanner.Analyzer.class.isAssignableFrom(p)) {
|
||||
implmnts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (implmnts) {
|
||||
this.analyzers.put(extension, (Analyzer) analyzer.newInstance());
|
||||
} else {
|
||||
String msg = String.format("Class '%s' does not implement org.codesecure.dependencycheck.scanner.Analyzer and cannot be loaded as an Analyzer for extension '%s'.", className, extension);
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.WARNING, msg);
|
||||
}
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (InstantiationException ex) {
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Map of the analyzers.
|
||||
*
|
||||
* @return the analyzers loaded
|
||||
*/
|
||||
public Map<String, Analyzer> getAnalyzers() {
|
||||
return analyzers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dependencies identified
|
||||
*
|
||||
* @return the dependencies identified
|
||||
*/
|
||||
public List<Dependency> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively.
|
||||
* Any dependencies identified are added to the dependency collection.
|
||||
*
|
||||
* @param path the path to a file or directory to be analyzed.
|
||||
*/
|
||||
public void scan(String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
scanDirectory(file);
|
||||
} else {
|
||||
scanFile(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scans files and directories.
|
||||
* Any dependencies identified are added to the dependency collection.
|
||||
*
|
||||
* @param dir the directory to scan.
|
||||
*/
|
||||
protected void scanDirectory(File dir) {
|
||||
File[] files = dir.listFiles();
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
scanDirectory(f);
|
||||
} else {
|
||||
scanFile(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a specified file. If a dependency is identified it is added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param file The file to scan.
|
||||
*/
|
||||
protected void scanFile(File file) {
|
||||
if (!file.isFile()) {
|
||||
String msg = String.format("Path passed to scanFile(File) is not a file: %s.", file.toString());
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.WARNING, msg);
|
||||
}
|
||||
String fileName = file.getName();
|
||||
String extension = getFileExtension(fileName);
|
||||
if (extension != null) {
|
||||
if (analyzers.containsKey(extension)) {
|
||||
Analyzer a = analyzers.get(extension);
|
||||
try {
|
||||
Dependency dependency = a.insepct(file);
|
||||
dependencies.add(dependency);
|
||||
} catch (IOException ex) {
|
||||
String msg = String.format("IOException occured while scanning the file '%s'.", file.toString());
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, msg, ex);
|
||||
}
|
||||
} else {
|
||||
String msg = String.format("No analyzer is configured for files of type '%s'. The file, '%s', was not analyzed.", extension, file.toString());
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.WARNING, msg);
|
||||
}
|
||||
} else {
|
||||
String msg = String.format("No files extension found on file '%s'. The file was not analyzed.", file.toString());
|
||||
Logger.getLogger(Scanner.class.getName()).log(Level.WARNING, msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file extension for a specified file.
|
||||
* @param fileName the file name to retrieve the file extension from.
|
||||
* @return the file extension.
|
||||
*/
|
||||
protected String getFileExtension(String fileName) {
|
||||
String ret = null;
|
||||
int pos = fileName.lastIndexOf(".");
|
||||
if (pos >= 0) {
|
||||
ret = fileName.substring(pos + 1, fileName.length());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.codesecure.dependencycheck.scanner</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* The scanner package contains the utilities to scan files and directories for
|
||||
* dependencies. Analyzers are used to inspect the identified dependencies and
|
||||
* collect Evidence. This evidence is then used to determine if the dependency
|
||||
* has a known CPE.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
104
src/main/java/org/codesecure/dependencycheck/utils/Checksum.java
Normal file
104
src/main/java/org/codesecure/dependencycheck/utils/Checksum.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Includes methods to generate the MD5 and SHA1 checksum.
|
||||
*
|
||||
* This code was copied from Real's How To. It has been slightly modified.
|
||||
*
|
||||
* Written and compiled by Réal Gagnon ©1998-2012
|
||||
*
|
||||
* @author Real's How To: http://www.rgagnon.com/javadetails/java-0416.html
|
||||
*
|
||||
*/
|
||||
public class Checksum {
|
||||
|
||||
/**
|
||||
* <p>Creates the cryptographic checksum of a given file using the specified alogirhtm.</p>
|
||||
* <p>This algorithm was copied and heavily modified from Real's How To: http://www.rgagnon.com/javadetails/java-0416.html</p>
|
||||
*
|
||||
* @param algorithm the algorithm to use to calculate the checksum
|
||||
* @param file the file to calculate the checksum for
|
||||
* @return the checksum
|
||||
* @throws FileNotFoundException when the file does not exist
|
||||
* @throws NoSuchAlgorithmException when an algorithm is specified that does not exist
|
||||
*/
|
||||
public static byte[] getChecksum(String algorithm, File file) throws FileNotFoundException, NoSuchAlgorithmException {
|
||||
InputStream fis = new FileInputStream(file);
|
||||
byte[] buffer = new byte[1024];
|
||||
MessageDigest complete = MessageDigest.getInstance(algorithm);
|
||||
int numRead;
|
||||
try {
|
||||
do {
|
||||
numRead = fis.read(buffer);
|
||||
if (numRead > 0) {
|
||||
complete.update(buffer, 0, numRead);
|
||||
}
|
||||
} while (numRead != -1);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Checksum.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Checksum.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
return complete.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the MD5 checksum of a specified file.
|
||||
*
|
||||
* @param file the file to generate the MD5 checksum
|
||||
* @return the hex representation of the MD5 hash
|
||||
* @throws FileNotFoundException when the file passed in does not exist
|
||||
* @throws NoSuchAlgorithmException when the MD5 algorithm is not available
|
||||
*/
|
||||
public static String getMD5Checksum(File file) throws FileNotFoundException, NoSuchAlgorithmException {
|
||||
byte[] b = getChecksum("MD5", file);
|
||||
return getHex(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the SHA1 checksum of a specified file.
|
||||
*
|
||||
* @param file the file to generate the MD5 checksum
|
||||
* @return the hex representation of the SHA1 hash
|
||||
* @throws FileNotFoundException when the file passed in does not exist
|
||||
* @throws NoSuchAlgorithmException when the SHA1 algorithm is not available
|
||||
*/
|
||||
public static String getSHA1Checksum(File file) throws FileNotFoundException, NoSuchAlgorithmException {
|
||||
byte[] b = getChecksum("SHA1", file);
|
||||
return getHex(b);
|
||||
}
|
||||
private static final String HEXES = "0123456789ABCDEF";
|
||||
|
||||
/**
|
||||
* <p>Converts a byte array into a hex string.</p>
|
||||
*
|
||||
* <p>This method was copied from <a href="http://www.rgagnon.com/javadetails/java-0596.html">http://www.rgagnon.com/javadetails/java-0596.html</a></p>
|
||||
*
|
||||
* @param raw a byte array
|
||||
* @return the hex representation of the byte array
|
||||
*/
|
||||
public static String getHex(byte[] raw) {
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder hex = new StringBuilder(2 * raw.length);
|
||||
for (final byte b : raw) {
|
||||
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
|
||||
}
|
||||
return hex.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.OptionGroup;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PosixParser;
|
||||
|
||||
/**
|
||||
* A utility to parse command line arguments for the DependencyCheck.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public final class CliParser {
|
||||
|
||||
/**
|
||||
* The command line.
|
||||
*/
|
||||
private CommandLine line = null;
|
||||
/**
|
||||
* The options for the command line parser.
|
||||
*/
|
||||
private Options options = createCommandLineOptions();
|
||||
/**
|
||||
* indicates whether the arguments are valid.
|
||||
*/
|
||||
boolean isValid = true;
|
||||
|
||||
/**
|
||||
* Parses the arguments passed in and captures the results for later use.
|
||||
*
|
||||
* @param args the command line arguments
|
||||
* @throws FileNotFoundException is thrown when a 'file' argument does not
|
||||
* point to a file that exists.
|
||||
* @throws ParseException is thrown when a Parse Exception occurs.
|
||||
*/
|
||||
public void parse(String[] args) throws FileNotFoundException, ParseException {
|
||||
line = parseArgs(args);
|
||||
|
||||
if (line != null) {
|
||||
validateArgs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the command line arguments.
|
||||
*
|
||||
* @param args the command line arguments
|
||||
* @return the results of parsing the command line arguments
|
||||
* @throws ParseException if the arguments are invalid
|
||||
*/
|
||||
private CommandLine parseArgs(String[] args) throws ParseException {
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine ln = parser.parse(options, args);
|
||||
return ln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the command line arguments are valid.
|
||||
*
|
||||
* @throws FileNotFoundException if there is a file specified by either the
|
||||
* SCAN or CPE command line arguments that does not exist.
|
||||
*/
|
||||
private void validateArgs() throws FileNotFoundException, ParseException {
|
||||
if (isLoadCPE()) {
|
||||
validatePathExists(getCpeFile());
|
||||
}
|
||||
if (isRunScan()) {
|
||||
validatePathExists(getScanFiles());
|
||||
if (!line.hasOption(ArgumentName.OUT)) {
|
||||
//TODO - need a new exception type here, this isn't really a parseexception.
|
||||
throw new ParseException("Scan cannot be run without specifying a directory to write the reports to via the 'out' argument.");
|
||||
} else {
|
||||
String p = line.getOptionValue(ArgumentName.OUT,"");
|
||||
File f = new File(p);
|
||||
if ("".equals(p) || !(f.exists() && f.isDirectory())) {
|
||||
//TODO - need a new exception type here, this isn't really a parseexception.
|
||||
throw new ParseException("A valid directory name must be specified for the 'out' argument.");
|
||||
}
|
||||
}
|
||||
if (!line.hasOption(ArgumentName.APPNAME)) {
|
||||
throw new ParseException("Scan cannot be run without specifying an application name via the 'app' argument.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether or not the path(s) points at a file that exists; if the
|
||||
* path(s) does not point to an existing file a FileNotFoundException is thrown.
|
||||
*
|
||||
* @param paths the paths to validate if they exists
|
||||
* @throws FileNoteFoundException is thrown if one of the paths being validated does not exist.
|
||||
*/
|
||||
private void validatePathExists(String[] paths) throws FileNotFoundException {
|
||||
for (String path : paths) {
|
||||
validatePathExists(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether or not the path points at a file that exists; if the
|
||||
* path does not point to an existing file a FileNotFoundException is thrown.
|
||||
*
|
||||
* @param paths the paths to validate if they exists
|
||||
* @throws FileNoteFoundException is thrown if the path being validated does not exist.
|
||||
*/
|
||||
private void validatePathExists(String path) throws FileNotFoundException {
|
||||
File f = new File(path);
|
||||
if (!f.exists()) {
|
||||
isValid = false;
|
||||
throw new FileNotFoundException("Invalid file argument: " + path);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Generates an Options collection that is used to parse the command line
|
||||
* and to display the help message.
|
||||
*
|
||||
* @return the command line options used for parsing the command line
|
||||
*/
|
||||
@SuppressWarnings("static-access")
|
||||
private Options createCommandLineOptions() {
|
||||
Option help = new Option(ArgumentName.HELP_SHORT, ArgumentName.HELP, false, "print this message");
|
||||
Option version = new Option(ArgumentName.VERSION_SHORT, ArgumentName.VERSION, false, "print the version information and exit");
|
||||
|
||||
Option appname = OptionBuilder.withArgName("name").hasArg().withLongOpt(ArgumentName.APPNAME).withDescription("the name of the application being scanned").create(ArgumentName.APPNAME_SHORT);
|
||||
|
||||
Option path = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.SCAN).withDescription("the path to scan").create(ArgumentName.SCAN_SHORT);
|
||||
|
||||
Option load = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.CPE).withDescription("load the CPE xml file").create(ArgumentName.CPE_SHORT);
|
||||
|
||||
Option out = OptionBuilder.withArgName("folder").hasArg().withLongOpt(ArgumentName.OUT).withDescription("the folder to write reports to.").create(ArgumentName.OUT_SHORT);
|
||||
|
||||
//TODO add the ability to load a properties file to override the defaults...
|
||||
//TODO add the ability to load the CVE entries.
|
||||
//TODO add a switch to auto-update CVE entries.
|
||||
//TODO add a switch to auto-update CPE entries.
|
||||
|
||||
OptionGroup og = new OptionGroup();
|
||||
og.addOption(path);
|
||||
og.addOption(load);
|
||||
|
||||
Options opts = new Options();
|
||||
opts.addOptionGroup(og);
|
||||
opts.addOption(out);
|
||||
opts.addOption(appname);
|
||||
opts.addOption(version);
|
||||
opts.addOption(help);
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the 'version' command line argument was passed in.
|
||||
*
|
||||
* @return whether or not the 'version' command line argument was passed in
|
||||
*/
|
||||
public boolean isGetVersion() {
|
||||
return (line != null) ? line.hasOption(ArgumentName.VERSION) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the 'help' command line argument was passed in.
|
||||
*
|
||||
* @return whether or not the 'help' command line argument was passed in
|
||||
*/
|
||||
public boolean isGetHelp() {
|
||||
return (line != null) ? line.hasOption(ArgumentName.HELP) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the 'cpe' command line argument was passed in.
|
||||
*
|
||||
* @return whether or not the 'cpe' command line argument was passed in
|
||||
*/
|
||||
public boolean isLoadCPE() {
|
||||
return (line != null) ? isValid && line.hasOption(ArgumentName.CPE) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the 'scan' command line argument was passed in.
|
||||
*
|
||||
* @return whether or not the 'scan' command line argument was passed in
|
||||
*/
|
||||
public boolean isRunScan() {
|
||||
return (line != null) ? isValid && line.hasOption(ArgumentName.SCAN) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the command line help message to the standard output.
|
||||
*/
|
||||
public void printHelp() {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp("DependencyCheck", options, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the file command line parameter(s) specified for the 'cpe' argument.
|
||||
*
|
||||
* @return the file paths specified on the command line
|
||||
*/
|
||||
public String getCpeFile() {
|
||||
return line.getOptionValue(ArgumentName.CPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the file command line parameter(s) specified for the 'scan' argument.
|
||||
*
|
||||
* @return the file paths specified on the command line for scan
|
||||
*/
|
||||
public String[] getScanFiles() {
|
||||
return line.getOptionValues(ArgumentName.SCAN);
|
||||
|
||||
}
|
||||
|
||||
public String getReportDirectory() {
|
||||
return line.getOptionValue(ArgumentName.OUT);
|
||||
}
|
||||
public String getApplicationName() {
|
||||
return line.getOptionValue(ArgumentName.APPNAME);
|
||||
}
|
||||
/**
|
||||
* <p>Prints the manifest information to standard output:</p>
|
||||
* <ul><li>Implementation-Title: ${pom.name}</li>
|
||||
* <li>Implementation-Version: ${pom.version}</li></ul>
|
||||
*/
|
||||
public void printVersionInfo() {
|
||||
String version = "DependencyCheck version unknown";
|
||||
|
||||
URLClassLoader cl = (URLClassLoader) this.getClass().getClassLoader();
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
URL url = cl.findResource("META-INF/MANIFEST.MF");
|
||||
is = url.openStream();
|
||||
Manifest manifest = new Manifest(is);
|
||||
Attributes atts = manifest.getMainAttributes();
|
||||
version = atts.getValue(Attributes.Name.IMPLEMENTATION_TITLE)
|
||||
+ " version "
|
||||
+ atts.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CliParser.class.getName()).log(Level.WARNING, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
is = null;
|
||||
} catch (Throwable ex) {
|
||||
Logger.getLogger(CliParser.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
System.out.println(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of static final strings that represent the possible command
|
||||
* line arguments.
|
||||
*/
|
||||
public static class ArgumentName {
|
||||
|
||||
public static final String SCAN = "scan";
|
||||
public static final String CPE = "cpe";
|
||||
public static final String OUT = "out";
|
||||
public static final String APPNAME = "app";
|
||||
public static final String VERSION = "version";
|
||||
public static final String HELP = "help";
|
||||
public static final String SCAN_SHORT = "s";
|
||||
public static final String CPE_SHORT = "c";
|
||||
public static final String OUT_SHORT = "o";
|
||||
public static final String VERSION_SHORT = "v";
|
||||
public static final String HELP_SHORT = "h";
|
||||
public static final String APPNAME_SHORT = "a";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A utility to download files from the Internet.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Downloader {
|
||||
|
||||
/**
|
||||
* Private constructor for utility class.
|
||||
*/
|
||||
private Downloader() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a file from a given URL and saves it to the outputPath.
|
||||
* @param url the URL of the file to download.
|
||||
* @param outputPath the path to the save the file to.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
public static void fetchFile(URL url, String outputPath) throws IOException {
|
||||
File f = new File(outputPath);
|
||||
fetchFile(url, f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a file from a given URL and saves it to the outputPath.
|
||||
* @param url the URL of the file to download.
|
||||
* @param outputPath the path to the save the file to.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
public static void fetchFile(URL url, File outputPath) throws IOException {
|
||||
url.openConnection();
|
||||
BufferedOutputStream writer = null;
|
||||
try {
|
||||
InputStream reader = url.openStream();
|
||||
writer = new BufferedOutputStream(new FileOutputStream(outputPath));
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead = 0;
|
||||
while ((bytesRead = reader.read(buffer)) > 0) {
|
||||
writer.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
writer.close();
|
||||
writer = null;
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(Downloader.class.getName()).log(Level.WARNING,
|
||||
"Error closing the writter in Downloader.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/*
|
||||
* This is an abstract filter that can be used to filter iterable list.
|
||||
*
|
||||
* This Filter class was copied from: http://erikras.com/2008/01/18/the-filter-pattern-java-conditional-abstraction-with-iterables/
|
||||
*
|
||||
* Erik Rasmussen - © 2006 - 2012 All Rights Reserved.
|
||||
* @author Erik Rasmussen https://plus.google.com/115403795880834599019/?rel=author
|
||||
*/
|
||||
|
||||
public abstract class Filter<T> {
|
||||
|
||||
public abstract boolean passes(T object);
|
||||
|
||||
public Iterator<T> filter(Iterator<T> iterator) {
|
||||
return new FilterIterator(iterator);
|
||||
}
|
||||
|
||||
public Iterable<T> filter(final Iterable<T> iterable) {
|
||||
return new Iterable<T>() {
|
||||
|
||||
public Iterator<T> iterator() {
|
||||
return filter(iterable.iterator());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class FilterIterator implements Iterator<T> {
|
||||
|
||||
private Iterator<T> iterator;
|
||||
private T next;
|
||||
|
||||
private FilterIterator(Iterator<T> iterator) {
|
||||
this.iterator = iterator;
|
||||
toNext();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
public T next() {
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
T returnValue = next;
|
||||
toNext();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void toNext() {
|
||||
next = null;
|
||||
while (iterator.hasNext()) {
|
||||
T item = iterator.next();
|
||||
if (item != null && passes(item)) {
|
||||
next = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
677
src/main/java/org/codesecure/dependencycheck/utils/SSDeep.java
Normal file
677
src/main/java/org/codesecure/dependencycheck/utils/SSDeep.java
Normal file
@@ -0,0 +1,677 @@
|
||||
/* ssdeep
|
||||
Copyright (C) 2006 ManTech International Corporation
|
||||
|
||||
$Id: fuzzy.c 97 2010-03-19 15:10:06Z jessekornblum $
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
The code in this file, and this file only, is based on SpamSum, part
|
||||
of the Samba project:
|
||||
http://www.samba.org/ftp/unpacked/junkcode/spamsum/
|
||||
|
||||
Because of where this file came from, any program that contains it
|
||||
must be licensed under the terms of the General Public License (GPL).
|
||||
See the file COPYING for details. The author's original comments
|
||||
about licensing are below:
|
||||
|
||||
|
||||
|
||||
this is a checksum routine that is specifically designed for spam.
|
||||
Copyright Andrew Tridgell <tridge@samba.org> 2002
|
||||
|
||||
This code is released under the GNU General Public License version 2
|
||||
or later. Alteratively, you may also use this code under the terms
|
||||
of the Perl Artistic license.
|
||||
|
||||
If you wish to distribute this code under the terms of a different
|
||||
free software license then please ask me. If there is a good reason
|
||||
then I will probably say yes.
|
||||
|
||||
*/
|
||||
|
||||
//package eu.scape_project.bitwiser.utils;
|
||||
//https://raw.github.com/openplanets/bitwiser/master/src/main/java/eu/scape_project/bitwiser/utils/SSDeep.java
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* SSDeep
|
||||
*
|
||||
* <p>
|
||||
* A Java version of the ssdeep algorithm, based on the fuzzy.c source
|
||||
* code, taken from version 2.6 of the ssdeep package.
|
||||
*
|
||||
* <p>
|
||||
* Transliteration/port to Java from C by...
|
||||
*
|
||||
* @author Andrew Jackson <Andrew.Jackson@bl.uk>
|
||||
*
|
||||
*/
|
||||
public class SSDeep {
|
||||
|
||||
public class FuzzyHash {
|
||||
/** the blocksize used by the program, */
|
||||
int blocksize;
|
||||
/** the hash for this blocksize */
|
||||
String hash;
|
||||
/** the hash for twice the blocksize, */
|
||||
String hash2;
|
||||
/** the filename. */
|
||||
String filename;
|
||||
}
|
||||
|
||||
/// Length of an individual fuzzy hash signature component
|
||||
public static final int SPAMSUM_LENGTH = 64;
|
||||
|
||||
/// The longest possible length for a fuzzy hash signature (without the filename)
|
||||
public static final int FUZZY_MAX_RESULT = (SPAMSUM_LENGTH + (SPAMSUM_LENGTH/2 + 20));
|
||||
|
||||
|
||||
public static final int MIN_BLOCKSIZE = 3;
|
||||
public static final int ROLLING_WINDOW = 7;
|
||||
|
||||
public static final int HASH_PRIME = 0x01000193;
|
||||
public static final int HASH_INIT = 0x28021967;
|
||||
|
||||
// Our input buffer when reading files to hash
|
||||
public static final int BUFFER_SIZE = 8192;
|
||||
|
||||
static class roll_state_class {
|
||||
int[] window = new int[ROLLING_WINDOW];
|
||||
int h1, h2, h3;
|
||||
int n;
|
||||
}
|
||||
private static roll_state_class roll_state = new roll_state_class();
|
||||
|
||||
|
||||
/*
|
||||
a rolling hash, based on the Adler checksum. By using a rolling hash
|
||||
we can perform auto resynchronisation after inserts/deletes
|
||||
|
||||
internally, h1 is the sum of the bytes in the window and h2
|
||||
is the sum of the bytes times the index
|
||||
|
||||
h3 is a shift/xor based rolling hash, and is mostly needed to ensure that
|
||||
we can cope with large blocksize values
|
||||
*/
|
||||
static int roll_hash(int c)
|
||||
{
|
||||
|
||||
// System.out.println(""+roll_state.h1+","+roll_state.h2+","+roll_state.h3);
|
||||
roll_state.h2 -= roll_state.h1;
|
||||
//roll_state.h2 = roll_state.h2 & 0x7fffffff;
|
||||
roll_state.h2 += ROLLING_WINDOW * c;
|
||||
//roll_state.h2 = roll_state.h2 & 0x7fffffff;
|
||||
|
||||
roll_state.h1 += c;
|
||||
//roll_state.h1 = roll_state.h1 & 0x7fffffff;
|
||||
roll_state.h1 -= roll_state.window[(roll_state.n % ROLLING_WINDOW)];
|
||||
//roll_state.h1 = roll_state.h1 & 0x7fffffff;
|
||||
|
||||
roll_state.window[roll_state.n % ROLLING_WINDOW] = (char)c;
|
||||
roll_state.n = (roll_state.n+1)%ROLLING_WINDOW;
|
||||
|
||||
/* The original spamsum AND'ed this value with 0xFFFFFFFF which
|
||||
in theory should have no effect. This AND has been removed
|
||||
for performance (jk) */
|
||||
roll_state.h3 = (roll_state.h3 << 5);// & 0xFFFFFFFF;
|
||||
roll_state.h3 ^= c;
|
||||
//roll_state.h3 = roll_state.h3 & 0x7FFFFFFF;
|
||||
//if( roll_state.h3 > 0xEFFFFFFF ) roll_state.h3 -= 0xEFFFFFFF;
|
||||
|
||||
long result = ((roll_state.h1 + roll_state.h2 + roll_state.h3));//&0x7FFFFFFF;
|
||||
//System.out.println("Result: "+result);
|
||||
//System.out.println("Result2: "+(result&0xFFFFFFFF));
|
||||
//System.out.println("Result3: "+(result&0x7FFFFFFF));
|
||||
|
||||
return (int) result;//&0xFFFFFFFF;
|
||||
}
|
||||
|
||||
/*
|
||||
reset the state of the rolling hash and return the initial rolling hash value
|
||||
*/
|
||||
static void roll_reset()
|
||||
{
|
||||
roll_state.h1 = 0;
|
||||
roll_state.h2 = 0;
|
||||
roll_state.h3 = 0;
|
||||
roll_state.n = 0;
|
||||
Arrays.fill(roll_state.window,(char)0);
|
||||
}
|
||||
|
||||
/* a simple non-rolling hash, based on the FNV hash */
|
||||
static int sum_hash(int c, int h)
|
||||
{
|
||||
h *= HASH_PRIME;
|
||||
//h = h & 0xFFFFFFFF;
|
||||
h ^= c;
|
||||
//h = h & 0xFFFFFFFF;
|
||||
return h;
|
||||
}
|
||||
|
||||
class ss_context {
|
||||
char[] ret;
|
||||
char[] p;
|
||||
long total_chars;
|
||||
int h, h2, h3;
|
||||
int j, n, i, k;
|
||||
int block_size;
|
||||
char[] ret2 = new char[SPAMSUM_LENGTH/2 + 1];
|
||||
}
|
||||
|
||||
|
||||
static void ss_destroy(ss_context ctx)
|
||||
{
|
||||
if (ctx.ret != null)
|
||||
ctx.ret = null;
|
||||
//free(ctx.ret);
|
||||
}
|
||||
|
||||
|
||||
static boolean ss_init(ss_context ctx, File handle)
|
||||
{
|
||||
if ( ctx == null )
|
||||
return true;
|
||||
|
||||
ctx.ret = new char[FUZZY_MAX_RESULT];
|
||||
if (ctx.ret == null)
|
||||
return true;
|
||||
|
||||
if (handle != null)
|
||||
ctx.total_chars = handle.length();
|
||||
|
||||
ctx.block_size = MIN_BLOCKSIZE;
|
||||
while (ctx.block_size * SPAMSUM_LENGTH < ctx.total_chars) {
|
||||
ctx.block_size = ctx.block_size * 2;
|
||||
}
|
||||
|
||||
System.out.println("bs:"+ctx.block_size);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static char[] b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
||||
|
||||
static void ss_engine(ss_context ctx,
|
||||
byte[] buffer,
|
||||
int buffer_size)
|
||||
{
|
||||
if (null == ctx || null == buffer)
|
||||
return;
|
||||
|
||||
for ( int i = 0 ; i < buffer_size ; ++i)
|
||||
{
|
||||
|
||||
/*
|
||||
at each character we update the rolling hash and
|
||||
the normal hash. When the rolling hash hits the
|
||||
reset value then we emit the normal hash as a
|
||||
element of the signature and reset both hashes
|
||||
*/
|
||||
|
||||
System.out.println(""+ctx.h+","+ctx.h2+","+ctx.h3);
|
||||
ctx.h = roll_hash(buffer[i]);// & 0x7FFFFFFF;
|
||||
ctx.h2 = sum_hash(buffer[i], ctx.h2);// & 0x7FFFFFFF;
|
||||
ctx.h3 = sum_hash(buffer[i], ctx.h3);// & 0x7FFFFFFF;
|
||||
|
||||
if (((0xFFFFFFFFl & ctx.h) % ctx.block_size) == (ctx.block_size-1)) {
|
||||
/* we have hit a reset point. We now emit a
|
||||
hash which is based on all chacaters in the
|
||||
piece of the message between the last reset
|
||||
point and this one */
|
||||
ctx.p[ctx.j] = b64[(int)((ctx.h2&0xFFFF) % 64)];
|
||||
System.out.println("::"+ctx.j+":"+new String(ctx.p));
|
||||
// for( char c : ctx.p ) {
|
||||
// System.out.print(c);
|
||||
// }
|
||||
// System.out.println();
|
||||
if (ctx.j < SPAMSUM_LENGTH-1) {
|
||||
/* we can have a problem with the tail
|
||||
overflowing. The easiest way to
|
||||
cope with this is to only reset the
|
||||
second hash if we have room for
|
||||
more characters in our
|
||||
signature. This has the effect of
|
||||
combining the last few pieces of
|
||||
the message into a single piece */
|
||||
|
||||
ctx.h2 = HASH_INIT;
|
||||
(ctx.j)++;
|
||||
}
|
||||
}
|
||||
|
||||
/* this produces a second signature with a block size
|
||||
of block_size*2. By producing dual signatures in
|
||||
this way the effect of small changes in the message
|
||||
size near a block size boundary is greatly reduced. */
|
||||
if (((0xFFFFFFFFl & ctx.h) % (ctx.block_size*2)) == ((ctx.block_size*2)-1)) {
|
||||
ctx.ret2[ctx.k] = b64[(int) (ctx.h3&0xFFFF % 64)];
|
||||
if (ctx.k < SPAMSUM_LENGTH/2-1) {
|
||||
ctx.h3 = HASH_INIT;
|
||||
(ctx.k)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean ss_update(ss_context ctx, File handle) throws IOException
|
||||
{
|
||||
int bytes_read = 0;
|
||||
byte[] buffer;
|
||||
|
||||
if (null == ctx || null == handle)
|
||||
return true;
|
||||
|
||||
buffer = new byte[BUFFER_SIZE];
|
||||
if (buffer == null)
|
||||
return true;
|
||||
|
||||
// snprintf(ctx.ret, 12, "%u:", ctx.block_size);
|
||||
ctx.ret = (ctx.block_size + ":").toCharArray();
|
||||
// ctx.p = ctx.ret + strlen(ctx.ret);
|
||||
ctx.p = new char[SPAMSUM_LENGTH];
|
||||
|
||||
//memset(ctx.p, 0, SPAMSUM_LENGTH+1);
|
||||
Arrays.fill(ctx.p, (char)0 );
|
||||
//memset(ctx.ret2, 0, sizeof(ctx.ret2.length));
|
||||
Arrays.fill(ctx.ret2, (char)0 );
|
||||
|
||||
ctx.k = ctx.j = 0;
|
||||
ctx.h3 = ctx.h2 = HASH_INIT;
|
||||
ctx.h = 0;
|
||||
roll_reset();
|
||||
|
||||
System.out.println("Opening file:"+handle);
|
||||
FileInputStream in = new FileInputStream(handle);
|
||||
// while ((bytes_read = fread(buffer,sizeof(byte),BUFFER_SIZE,handle)) > 0)
|
||||
while (in.available() > 0 )
|
||||
{
|
||||
bytes_read = in.read(buffer);
|
||||
ss_engine(ctx,buffer,bytes_read);
|
||||
}
|
||||
|
||||
if (ctx.h != 0)
|
||||
{
|
||||
ctx.p[ctx.j] = b64[(int) ((ctx.h2 & 0xFFFF) % 64)];
|
||||
ctx.ret2[ctx.k] = b64[(int) ((ctx.h3 &0xFFFF) % 64)];
|
||||
}
|
||||
|
||||
// strcat(ctx.p+ctx.j, ":");
|
||||
// strcat(ctx.p+ctx.j, ctx.ret2);
|
||||
ctx.ret = (new String(ctx.ret) + new String(ctx.p) + ":" + new String(ctx.ret2)).toCharArray();
|
||||
|
||||
// free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
boolean fuzzy_hash_file(File handle) throws IOException
|
||||
{
|
||||
ss_context ctx;
|
||||
int filepos;
|
||||
boolean done = false;
|
||||
|
||||
if (null == handle)
|
||||
return true;
|
||||
|
||||
ctx = new ss_context();
|
||||
if (ctx == null)
|
||||
return true;
|
||||
|
||||
// filepos = ftello(handle);
|
||||
|
||||
ss_init(ctx, handle);
|
||||
System.out.println("bs-pre:"+ctx.block_size);
|
||||
|
||||
while (!done)
|
||||
{
|
||||
// if (fseeko(handle,0,SEEK_SET))
|
||||
// return true;
|
||||
|
||||
ss_update(ctx,handle);
|
||||
|
||||
System.out.println("RESULT:"+new String(ctx.ret));
|
||||
|
||||
// our blocksize guess may have been way off - repeat if necessary
|
||||
if (ctx.block_size > MIN_BLOCKSIZE && ctx.j < SPAMSUM_LENGTH/2)
|
||||
ctx.block_size = ctx.block_size / 2;
|
||||
else
|
||||
done = true;
|
||||
}
|
||||
|
||||
System.out.println("bs-post:"+ctx.block_size);
|
||||
// strncpy(result,ctx.ret,FUZZY_MAX_RESULT);
|
||||
|
||||
System.out.println("RESULT:"+new String(ctx.ret));
|
||||
|
||||
ss_destroy(ctx);
|
||||
// free(ctx);
|
||||
|
||||
// if (fseeko(handle,filepos,SEEK_SET))
|
||||
// return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean fuzzy_hash_filename(String filename) throws IOException
|
||||
{
|
||||
boolean status;
|
||||
|
||||
if (null == filename)
|
||||
return true;
|
||||
|
||||
File handle = new File(filename);//,"rb");
|
||||
if (null == handle)
|
||||
return true;
|
||||
|
||||
status = fuzzy_hash_file(handle);
|
||||
|
||||
// fclose(handle);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
boolean fuzzy_hash_buf(byte[] buf,
|
||||
int buf_len,
|
||||
char[] result)
|
||||
{
|
||||
ss_context ctx = new ss_context();
|
||||
boolean done = false;
|
||||
|
||||
if (buf == null)
|
||||
return true;
|
||||
|
||||
ctx.total_chars = buf_len;
|
||||
ss_init(ctx, null);
|
||||
|
||||
System.out.println("total_chars: "+ctx.total_chars);
|
||||
|
||||
while (!done)
|
||||
{
|
||||
// snprintf(ctx.ret, 12, "%u:", ctx.block_size);
|
||||
// ctx.p = ctx.ret + strlen(ctx.ret);
|
||||
ctx.p = new char[SPAMSUM_LENGTH+1]; // TODO Duplication!
|
||||
|
||||
// memset(ctx.p, 0, SPAMSUM_LENGTH+1);
|
||||
// memset(ctx.ret2, 0, sizeof(ctx.ret2));
|
||||
|
||||
ctx.k = ctx.j = 0;
|
||||
ctx.h3 = ctx.h2 = HASH_INIT;
|
||||
ctx.h = 0;
|
||||
roll_reset();
|
||||
|
||||
System.out.println("h:"+ctx.h);
|
||||
System.out.println("h2:"+ctx.h2);
|
||||
|
||||
ss_engine(ctx,buf,buf_len);
|
||||
|
||||
/* our blocksize guess may have been way off - repeat if necessary */
|
||||
if (ctx.block_size > MIN_BLOCKSIZE && ctx.j < SPAMSUM_LENGTH/2)
|
||||
ctx.block_size = ctx.block_size / 2;
|
||||
else
|
||||
done = true;
|
||||
|
||||
System.out.println("h:"+ctx.h);
|
||||
System.out.println("h2:"+ctx.h2);
|
||||
System.out.println("h3:"+ctx.h3);
|
||||
System.out.println("bs:"+ctx.block_size);
|
||||
System.out.println("ret:"+new String(ctx.ret));
|
||||
System.out.println("p:"+new String(ctx.p));
|
||||
System.out.println("ret2:"+new String(ctx.ret2));
|
||||
if (ctx.h != 0)
|
||||
{
|
||||
ctx.p[ctx.j] = b64[(int) ((ctx.h2&0xFFFF) % 64)];
|
||||
ctx.ret2[ctx.k] = b64[(int) ((ctx.h3&0xFFFF) % 64)];
|
||||
}
|
||||
|
||||
// strcat(ctx.p+ctx.j, ":");
|
||||
// strcat(ctx.p+ctx.j, ctx.ret2);
|
||||
}
|
||||
|
||||
|
||||
// strncpy(result,ctx.ret,FUZZY_MAX_RESULT);
|
||||
System.out.println("bs:"+ctx.block_size);
|
||||
System.out.println("ret:"+new String(ctx.ret));
|
||||
System.out.println("p:"+new String(ctx.p));
|
||||
System.out.println("ret2:"+new String(ctx.ret2));
|
||||
System.out.println("h3:"+ctx.h3);
|
||||
result = ctx.ret;
|
||||
|
||||
ss_destroy(ctx);
|
||||
// free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
we only accept a match if we have at least one common substring in
|
||||
the signature of length ROLLING_WINDOW. This dramatically drops the
|
||||
false positive rate for low score thresholds while having
|
||||
negligable affect on the rate of spam detection.
|
||||
|
||||
return 1 if the two strings do have a common substring, 0 otherwise
|
||||
*/
|
||||
static int has_common_substring(char[] s1, char[] s2)
|
||||
{
|
||||
int i, j;
|
||||
int num_hashes;
|
||||
long[] hashes = new long[SPAMSUM_LENGTH];
|
||||
|
||||
/* there are many possible algorithms for common substring
|
||||
detection. In this case I am re-using the rolling hash code
|
||||
to act as a filter for possible substring matches */
|
||||
|
||||
roll_reset();
|
||||
// memset(hashes, 0, sizeof(hashes));
|
||||
|
||||
/* first compute the windowed rolling hash at each offset in
|
||||
the first string */
|
||||
for (i=0;s1[i] != 0;i++)
|
||||
{
|
||||
hashes[i] = roll_hash((char)s1[i]);
|
||||
}
|
||||
num_hashes = i;
|
||||
|
||||
roll_reset();
|
||||
|
||||
/* now for each offset in the second string compute the
|
||||
rolling hash and compare it to all of the rolling hashes
|
||||
for the first string. If one matches then we have a
|
||||
candidate substring match. We then confirm that match with
|
||||
a direct string comparison */
|
||||
for (i=0;s2[i] != 0;i++) {
|
||||
long h = roll_hash((char)s2[i]);
|
||||
if (i < ROLLING_WINDOW-1) continue;
|
||||
for (j=ROLLING_WINDOW-1;j<num_hashes;j++)
|
||||
{
|
||||
if (hashes[j] != 0 && hashes[j] == h)
|
||||
{
|
||||
/* we have a potential match - confirm it */
|
||||
/*FIXME
|
||||
if (strlen(s2+i-(ROLLING_WINDOW-1)) >= ROLLING_WINDOW &&
|
||||
strncmp(s2+i-(ROLLING_WINDOW-1),
|
||||
s1+j-(ROLLING_WINDOW-1),
|
||||
ROLLING_WINDOW) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// eliminate sequences of longer than 3 identical characters. These
|
||||
// sequences contain very little information so they tend to just bias
|
||||
// the result unfairly
|
||||
static char[] eliminate_sequences(String string)
|
||||
{
|
||||
char[] str = string.toCharArray();
|
||||
StringBuffer ret = new StringBuffer();
|
||||
|
||||
// Do not include repeats:
|
||||
for (int i=3;i<str.length;i++) {
|
||||
if (str[i] != str[i-1] ||
|
||||
str[i] != str[i-2] ||
|
||||
str[i] != str[i-3]) {
|
||||
ret.append(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret.toString().toCharArray();
|
||||
}
|
||||
|
||||
/*
|
||||
this is the low level string scoring algorithm. It takes two strings
|
||||
and scores them on a scale of 0-100 where 0 is a terrible match and
|
||||
100 is a great match. The block_size is used to cope with very small
|
||||
messages.
|
||||
*/
|
||||
static int score_strings(char[] s1, char[] s2, int block_size)
|
||||
{
|
||||
int score = 0;
|
||||
int len1, len2;
|
||||
|
||||
len1 = s1.length;
|
||||
len2 = s2.length;
|
||||
|
||||
if (len1 > SPAMSUM_LENGTH || len2 > SPAMSUM_LENGTH) {
|
||||
/* not a real spamsum signature? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the two strings must have a common substring of length
|
||||
ROLLING_WINDOW to be candidates */
|
||||
if (has_common_substring(s1, s2) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* compute the edit distance between the two strings. The edit distance gives
|
||||
us a pretty good idea of how closely related the two strings are */
|
||||
score = StringUtils.getLevenshteinDistance(new String(s1), new String(s2));
|
||||
|
||||
/* scale the edit distance by the lengths of the two
|
||||
strings. This changes the score to be a measure of the
|
||||
proportion of the message that has changed rather than an
|
||||
absolute quantity. It also copes with the variability of
|
||||
the string lengths. */
|
||||
score = (score * SPAMSUM_LENGTH) / (len1 + len2);
|
||||
|
||||
/* at this stage the score occurs roughly on a 0-64 scale,
|
||||
* with 0 being a good match and 64 being a complete
|
||||
* mismatch */
|
||||
|
||||
/* rescale to a 0-100 scale (friendlier to humans) */
|
||||
score = (100 * score) / 64;
|
||||
|
||||
/* it is possible to get a score above 100 here, but it is a
|
||||
really terrible match */
|
||||
if (score >= 100) return 0;
|
||||
|
||||
/* now re-scale on a 0-100 scale with 0 being a poor match and
|
||||
100 being a excellent match. */
|
||||
score = 100 - score;
|
||||
|
||||
// printf ("len1: %"PRIu32" len2: %"PRIu32"\n", len1, len2);
|
||||
|
||||
/* when the blocksize is small we don't want to exaggerate the match size */
|
||||
if (score > block_size/MIN_BLOCKSIZE * Math.min(len1, len2)) {
|
||||
score = block_size/MIN_BLOCKSIZE * Math.min(len1, len2);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/*
|
||||
given two spamsum strings return a value indicating the degree to which they match.
|
||||
*/
|
||||
int fuzzy_compare(FuzzyHash fh1, FuzzyHash fh2 )
|
||||
{
|
||||
int score = 0;
|
||||
char[] s1_1, s1_2;
|
||||
char[] s2_1, s2_2;
|
||||
|
||||
// if the blocksizes don't match then we are comparing
|
||||
// apples to oranges. This isn't an 'error' per se. We could
|
||||
// have two valid signatures, but they can't be compared.
|
||||
if (fh1.blocksize != fh2.blocksize &&
|
||||
fh1.blocksize != fh2.blocksize*2 &&
|
||||
fh2.blocksize != fh1.blocksize*2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// there is very little information content is sequences of
|
||||
// the same character like 'LLLLL'. Eliminate any sequences
|
||||
// longer than 3. This is especially important when combined
|
||||
// with the has_common_substring() test below.
|
||||
s1_1 = eliminate_sequences(fh1.hash+1);
|
||||
s2_1 = eliminate_sequences(fh2.hash+1);
|
||||
|
||||
s1_2 = eliminate_sequences(fh1.hash2+1);
|
||||
s2_2 = eliminate_sequences(fh1.hash2+1);
|
||||
|
||||
// each signature has a string for two block sizes. We now
|
||||
// choose how to combine the two block sizes. We checked above
|
||||
// that they have at least one block size in common
|
||||
if (fh1.blocksize == fh2.blocksize) {
|
||||
int score1, score2;
|
||||
score1 = score_strings(s1_1, s2_1, fh1.blocksize);
|
||||
score2 = score_strings(s1_2, s2_2, fh2.blocksize);
|
||||
|
||||
// s.block_size = fh1.blocksize;
|
||||
|
||||
score = Math.max(score1, score2);
|
||||
} else if (fh1.blocksize == fh2.blocksize*2) {
|
||||
|
||||
score = score_strings(s1_1, s2_2, fh1.blocksize);
|
||||
// s.block_size = fh1.blocksize;
|
||||
} else {
|
||||
|
||||
score = score_strings(s1_2, s2_1, fh2.blocksize);
|
||||
// s.block_size = fh2.blocksize;
|
||||
}
|
||||
|
||||
return (int)score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main class for quick testing.
|
||||
* @param args
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void main( String[] args ) throws IOException {
|
||||
SSDeep ssd = new SSDeep();
|
||||
byte[] b2 = "Hello World how are you today...\n".getBytes();
|
||||
byte[] b3 = "Helli".getBytes();
|
||||
char[] h1 = null;
|
||||
boolean t1 = ssd.fuzzy_hash_buf(b2, b2.length, h1);
|
||||
System.out.println("Got "+h1);
|
||||
ssd.fuzzy_hash_file(new File("test"));
|
||||
//ssd.fuzzy_hash_file(new File("pom.xml"));
|
||||
}
|
||||
}
|
||||
139
src/main/java/org/codesecure/dependencycheck/utils/Settings.java
Normal file
139
src/main/java/org/codesecure/dependencycheck/utils/Settings.java
Normal file
@@ -0,0 +1,139 @@
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
/*
|
||||
* This file is part of DependencyCheck.
|
||||
*
|
||||
* DependencyCheck is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DependencyCheck is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A simple settings container that wraps the dependencycheck.properties file.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Settings {
|
||||
|
||||
/**
|
||||
* The collection of keys used within the properties file.
|
||||
*/
|
||||
public abstract class KEYS {
|
||||
|
||||
/**
|
||||
* The properties key for the path where the CPE Lucene Index will be stored.
|
||||
*/
|
||||
public static final String CPE_INDEX = "index.cpe";
|
||||
/**
|
||||
* The properties key for the URL to the CPE.
|
||||
*/
|
||||
public static final String CPE_URL = "index.cpe.url";
|
||||
/**
|
||||
* The properties key for the path where the CCE Lucene Index will be stored.
|
||||
*/
|
||||
public static final String CVE_INDEX = "index.cve";
|
||||
/**
|
||||
* The properties key for the path where the OSVDB Lucene Index will be stored.
|
||||
*/
|
||||
public static final String OSVDB_INDEX = "index.osvdb";
|
||||
/**
|
||||
* The properties key prefix for the analyzer assocations.
|
||||
*/
|
||||
public static final String FILE_EXTENSION_ANALYZER_ASSOCIATION_PREFIX = "file.extension.analyzer.association.";
|
||||
}
|
||||
private static final String PROPERTIES_FILE = "dependencycheck.properties";
|
||||
private static Settings instance = new Settings();
|
||||
private Properties props = null;
|
||||
|
||||
/**
|
||||
* Private contructor for the Settings class. This class loads the properties files.
|
||||
*/
|
||||
private Settings() {
|
||||
InputStream in = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
|
||||
props = new Properties();
|
||||
try {
|
||||
props.load(in);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value from the properties file. If the value was specified as a
|
||||
* system property or passed in via the -Dprop=value argument - this method
|
||||
* will return the value from the system properties before the values in
|
||||
* the contained configuration file.
|
||||
*
|
||||
* @param key the key to lookup within the properties file.
|
||||
* @return the property from the properties file.
|
||||
*/
|
||||
public static String getString(String key) {
|
||||
return System.getProperty(key, instance.props.getProperty(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of properties selected by a given prefix. For isntance
|
||||
* if you have five properties that started off with "org.codesecure.name"
|
||||
* you could get a collection of those properties by calling this method.
|
||||
*
|
||||
* NOTE: The prefix is removed from the given properties when returned.
|
||||
*
|
||||
* @param prefix the prefix used to search the property collections for.
|
||||
* @return a Map of properties found.
|
||||
*/
|
||||
public static Map<String, String> getPropertiesByPrefix(String prefix) {
|
||||
Map<String, String> ret = new HashMap<String, String>();
|
||||
|
||||
Properties properties = instance.props;
|
||||
for (Enumeration<Object> e = properties.keys(); e.hasMoreElements(); ) {
|
||||
Object o = e.nextElement();
|
||||
if (o instanceof String) {
|
||||
String key = (String) o;
|
||||
if (key.startsWith(prefix)) {
|
||||
String ext = key.substring(prefix.length());
|
||||
ret.put(ext, properties.getProperty(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
properties = System.getProperties();
|
||||
for (Enumeration<Object> e = properties.keys(); e.hasMoreElements(); ) {
|
||||
Object o = e.nextElement();
|
||||
if (o instanceof String) {
|
||||
String key = (String) o;
|
||||
if (key.startsWith(prefix)) {
|
||||
String ext = key.substring(prefix.length() + 1);
|
||||
ret.put(ext, properties.getProperty(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// public static boolean getBoolean(String key) {
|
||||
// return Boolean.parseBoolean(instance.props.getProperty(key));
|
||||
// }
|
||||
// public static long getLong(String key) {
|
||||
// return Long.parseLong(instance.props.getProperty(key));
|
||||
// }
|
||||
// public static int getInt(String key) {
|
||||
// return Integer.parseInt(instance.props.getProperty(key));
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.codesecure.dependencycheck.utils</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Includes various utility classes such as a Settings wrapper, a CLI Parser,
|
||||
* a Checksum utility, etc.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
9
src/main/resources/dependencycheck.properties
Normal file
9
src/main/resources/dependencycheck.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
# To change this template, choose Tools | Templates
|
||||
# and open the template in the editor.
|
||||
|
||||
index.cpe=store/cpe
|
||||
index.cpe.url=http://static.nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.2.xml
|
||||
index.cve=store/cve
|
||||
index.osvdb=store/osvdb
|
||||
|
||||
file.extension.analyzer.association.jar=org.codesecure.dependencycheck.scanner.JarAnalyzer
|
||||
321
src/main/resources/templates/HtmlReport.vsl
Normal file
321
src/main/resources/templates/HtmlReport.vsl
Normal file
File diff suppressed because one or more lines are too long
BIN
src/main/resources/templates/img/minus.gif
Normal file
BIN
src/main/resources/templates/img/minus.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 B |
BIN
src/main/resources/templates/img/plus.gif
Normal file
BIN
src/main/resources/templates/img/plus.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 B |
263
src/main/resources/templates/sample.html
Normal file
263
src/main/resources/templates/sample.html
Normal file
File diff suppressed because one or more lines are too long
2
src/main/resources/templates/scripts/jquery-1.8.0.min.js
vendored
Normal file
2
src/main/resources/templates/scripts/jquery-1.8.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
src/site/site.xml
Normal file
11
src/site/site.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project name="DependencyCheck">
|
||||
<bannerLeft>
|
||||
<name>DependencyCheck</name>
|
||||
</bannerLeft>
|
||||
<bannerRight>
|
||||
</bannerRight>
|
||||
<body>
|
||||
<menu ref="reports" />
|
||||
</body>
|
||||
</project>
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.data;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import junit.framework.TestCase;
|
||||
import org.codesecure.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public abstract class BaseIndexTestCase extends TestCase {
|
||||
|
||||
public BaseIndexTestCase(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
ensureIndexExists();
|
||||
}
|
||||
|
||||
protected void ensureIndexExists() throws Exception {
|
||||
String indexPath = Settings.getString("index.cpe");
|
||||
java.io.File f = new File(indexPath);
|
||||
if (!f.exists()) {
|
||||
f.mkdirs();
|
||||
FileInputStream fis = null;
|
||||
ZipInputStream zin = null;
|
||||
try {
|
||||
File path = new File(this.getClass().getClassLoader().getResource("index.cpe.zip").getPath());
|
||||
fis = new FileInputStream(path);
|
||||
zin = new ZipInputStream(new BufferedInputStream(fis));
|
||||
ZipEntry entry;
|
||||
while ((entry = zin.getNextEntry()) != null) {
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
int BUFFER = 2048;
|
||||
String outputName = indexPath + File.separatorChar + entry.getName();
|
||||
FileOutputStream fos = null;
|
||||
BufferedOutputStream dest = null;
|
||||
try {
|
||||
File o = new File(outputName);
|
||||
// File oPath = new File(o.getParent());
|
||||
// if (!oPath.exists()) {
|
||||
// oPath.mkdir();
|
||||
// }
|
||||
o.createNewFile();
|
||||
fos = new FileOutputStream(o,false);
|
||||
dest = new BufferedOutputStream(fos, BUFFER);
|
||||
byte data[] = new byte[BUFFER];
|
||||
int count;
|
||||
while ((count = zin.read(data, 0, BUFFER)) != -1) {
|
||||
dest.write(data, 0, count);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
String ignore = ex.getMessage();
|
||||
} finally {
|
||||
try {
|
||||
dest.flush();
|
||||
dest.close();
|
||||
dest = null;
|
||||
} catch (Throwable ex) { String ignore = ex.getMessage(); }
|
||||
try {
|
||||
fos.close();
|
||||
fos = null;
|
||||
} catch (Throwable ex) { String ignore = ex.getMessage(); }
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (zin!=null) {
|
||||
zin.close();
|
||||
}
|
||||
zin = null;
|
||||
} catch (Throwable ex) { String ignore = ex.getMessage(); }
|
||||
try {
|
||||
if (fis!=null) {
|
||||
fis.close();
|
||||
}
|
||||
fis = null;
|
||||
} catch (Throwable ex) { String ignore = ex.getMessage(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.data;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class LuceneUtilsTest {
|
||||
|
||||
public LuceneUtilsTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of appendEscapedLuceneQuery method, of class LuceneUtils.
|
||||
*/
|
||||
@Test
|
||||
public void testAppendEscapedLuceneQuery() {
|
||||
System.out.println("appendEscapedLuceneQuery");
|
||||
StringBuilder buf = new StringBuilder();
|
||||
CharSequence text = "test encoding + - & | ! ( ) { } [ ] ^ \" ~ * ? : \\";
|
||||
String expResult = "test encoding \\+ \\- \\& \\| \\! \\( \\) \\{ \\} \\[ \\] \\^ \\\" \\~ \\* \\? \\: \\\\";
|
||||
LuceneUtils.appendEscapedLuceneQuery(buf, text);
|
||||
assertEquals(expResult, buf.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of escapeLuceneQuery method, of class LuceneUtils.
|
||||
*/
|
||||
@Test
|
||||
public void testEscapeLuceneQuery() {
|
||||
System.out.println("escapeLuceneQuery");
|
||||
CharSequence text = "test encoding + - & | ! ( ) { } [ ] ^ \" ~ * ? : \\";
|
||||
String expResult = "test encoding \\+ \\- \\& \\| \\! \\( \\) \\{ \\} \\[ \\] \\^ \\\" \\~ \\* \\? \\: \\\\";
|
||||
String result = LuceneUtils.escapeLuceneQuery(text);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.queryParser.ParseException;
|
||||
import org.codesecure.dependencycheck.data.BaseIndexTestCase;
|
||||
import org.codesecure.dependencycheck.scanner.Dependency;
|
||||
import org.codesecure.dependencycheck.scanner.JarAnalyzer;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeremy
|
||||
*/
|
||||
public class CPEQueryTest extends BaseIndexTestCase {
|
||||
|
||||
public CPEQueryTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of locate method, of class CPEQuery.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testLocate() throws Exception {
|
||||
System.out.println("locate");
|
||||
String vendor = "apache software foundation";
|
||||
String product = "struts 2 core";
|
||||
String version = "2.1.2";
|
||||
CPEQuery instance = new CPEQuery();
|
||||
instance.open();
|
||||
String expResult = "cpe:/a:apache:struts:2.1.2";
|
||||
List<Entry> result = instance.searchCPE(vendor, product, version);
|
||||
assertEquals(expResult, result.get(0).getName());
|
||||
|
||||
//TODO - yeah, not a very good test as the results are the same with or without weighting...
|
||||
List<String> productWeightings = new ArrayList<String>(1);
|
||||
productWeightings.add("struts2");
|
||||
|
||||
List<String> vendorWeightings = new ArrayList<String>(1);
|
||||
vendorWeightings.add("apache");
|
||||
|
||||
result = instance.searchCPE(vendor, product, version,productWeightings,vendorWeightings);
|
||||
assertEquals(expResult, result.get(0).getName());
|
||||
|
||||
vendor = "apache software foundation";
|
||||
product = "struts 2 core";
|
||||
version = "2.3.1.2";
|
||||
|
||||
//yes, this isn't right. we verify this with another method later
|
||||
expResult = "cpe:/a:apache:struts";
|
||||
result = instance.searchCPE(vendor, product, version);
|
||||
boolean startsWith = result.get(0).getName().startsWith(expResult);
|
||||
assertTrue("CPE does not begin with apache struts",startsWith);
|
||||
instance.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests of buildSearch of class CPEQuery.
|
||||
* @throws IOException is thrown when an IO Exception occurs.
|
||||
* @throws CorruptIndexException is thrown when the index is corrupt.
|
||||
* @throws ParseException is thrown when a parse exception occurs
|
||||
*/
|
||||
@Test
|
||||
public void testBuildSearch() throws IOException, CorruptIndexException, ParseException {
|
||||
System.out.println("buildSearch");
|
||||
List<String> productWeightings = new ArrayList<String>(1);
|
||||
productWeightings.add("struts2");
|
||||
|
||||
List<String> vendorWeightings = new ArrayList<String>(1);
|
||||
vendorWeightings.add("apache");
|
||||
|
||||
String vendor = "apache software foundation";
|
||||
String product = "struts 2 core";
|
||||
String version = "2.1.2";
|
||||
CPEQuery instance = new CPEQuery();
|
||||
|
||||
String queryText = instance.buildSearch(vendor, product, version, null, null);
|
||||
String expResult = " product:( struts 2 core ) vendor:( apache software foundation ) version:(2.1.2)";
|
||||
assertTrue(expResult.equals(queryText));
|
||||
|
||||
queryText = instance.buildSearch(vendor, product, version, null, productWeightings);
|
||||
expResult = " product:( struts^5 struts2^5 2 core ) vendor:( apache software foundation ) version:(2.1.2^0.2 )";
|
||||
assertTrue(expResult.equals(queryText));
|
||||
|
||||
queryText = instance.buildSearch(vendor, product, version,vendorWeightings,null);
|
||||
expResult = " product:( struts 2 core ) vendor:( apache^5 software foundation ) version:(2.1.2^0.2 )";
|
||||
assertTrue(expResult.equals(queryText));
|
||||
|
||||
queryText = instance.buildSearch(vendor, product, version, vendorWeightings, productWeightings);
|
||||
expResult = " product:( struts^5 struts2^5 2 core ) vendor:( apache^5 software foundation ) version:(2.1.2^0.2 )";
|
||||
assertTrue(expResult.equals(queryText));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of open method, of class CPEQuery.
|
||||
* @throws Exception is thrown when an exception occurs
|
||||
*/
|
||||
@Test
|
||||
public void testOpen() throws Exception {
|
||||
System.out.println("open");
|
||||
CPEQuery instance = new CPEQuery();
|
||||
assertFalse(instance.isOpen());
|
||||
instance.open();
|
||||
assertTrue(instance.isOpen());
|
||||
instance.close();
|
||||
assertFalse(instance.isOpen());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test of determineCPE method, of class CPEQuery.
|
||||
* @throws Exception is thrown when an exception occurs
|
||||
*/
|
||||
@Test
|
||||
public void testDetermineCPE() throws Exception {
|
||||
System.out.println("determineCPE");
|
||||
File file = new File(this.getClass().getClassLoader().getResource("struts2-core-2.1.2.jar").getPath());
|
||||
JarAnalyzer jarAnalyzer = new JarAnalyzer();
|
||||
Dependency depends = jarAnalyzer.insepct(file);
|
||||
CPEQuery instance = new CPEQuery();
|
||||
instance.open();
|
||||
String expResult = "cpe:/a:apache:struts:2.1.2";
|
||||
instance.determineCPE(depends);
|
||||
instance.close();
|
||||
assertTrue(depends.getCPEs().contains(expResult));
|
||||
assertTrue(depends.getCPEs().size()==1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of searchCPE method, of class CPEQuery.
|
||||
* @throws Exception is thrown when an exception occurs
|
||||
*/
|
||||
@Test
|
||||
public void testSearchCPE_3args() throws Exception {
|
||||
System.out.println("searchCPE - 3 args");
|
||||
System.out.println("searchCPE");
|
||||
String vendor = "apache software foundation";
|
||||
String product = "struts 2 core";
|
||||
String version = "2.1.2";
|
||||
CPEQuery instance = new CPEQuery();
|
||||
instance.open();
|
||||
String expResult = "cpe:/a:apache:struts:2.1.2";
|
||||
List<Entry> result = instance.searchCPE(vendor, product, version);
|
||||
assertEquals(expResult, result.get(0).getName());
|
||||
|
||||
vendor = "apache software foundation";
|
||||
product = "struts 2 core";
|
||||
version = "2.3.1.2";
|
||||
|
||||
expResult = "cpe:/a:apache:struts";
|
||||
result = instance.searchCPE(vendor, product, version);
|
||||
boolean startsWith = result.get(0).getName().startsWith(expResult);
|
||||
assertTrue("CPE Does not start with apache struts.", startsWith);
|
||||
|
||||
instance.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of searchCPE method, of class CPEQuery.
|
||||
* @throws Exception is thrown when an exception occurs
|
||||
*/
|
||||
@Test
|
||||
public void testSearchCPE_5args() throws Exception {
|
||||
System.out.println("searchCPE - 5 args");
|
||||
String vendor = "apache software foundation";
|
||||
String product = "struts 2 core";
|
||||
String version = "2.1.2";
|
||||
String expResult = "cpe:/a:apache:struts:2.1.2";
|
||||
|
||||
CPEQuery instance = new CPEQuery();
|
||||
instance.open();
|
||||
|
||||
//TODO - yeah, not a very good test as the results are the same with or without weighting...
|
||||
List<String> productWeightings = new ArrayList<String>(1);
|
||||
productWeightings.add("struts2");
|
||||
|
||||
List<String> vendorWeightings = new ArrayList<String>(1);
|
||||
vendorWeightings.add("apache");
|
||||
|
||||
List<Entry> result = instance.searchCPE(vendor, product, version,productWeightings,vendorWeightings);
|
||||
assertEquals(expResult, result.get(0).getName());
|
||||
|
||||
|
||||
instance.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
|
||||
import org.codesecure.dependencycheck.data.cpe.Entry;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class EntryTest extends TestCase {
|
||||
|
||||
public EntryTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setName method, of class Entry.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
public void testSetName() throws Exception {
|
||||
System.out.println("setName");
|
||||
String name = "cpe:/a:apache:struts:1.1:rc2";
|
||||
|
||||
Entry instance = new Entry();
|
||||
instance.setName(name);
|
||||
|
||||
assertEquals(name,instance.getName());
|
||||
assertEquals("apache", instance.getVendor());
|
||||
assertEquals("struts", instance.getProduct());
|
||||
assertEquals("1.1", instance.getVersion());
|
||||
assertEquals("rc2", instance.getRevision());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.data.cpe;
|
||||
|
||||
import org.codesecure.dependencycheck.data.BaseIndexTestCase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeremy
|
||||
*/
|
||||
public class IndexTestCase extends BaseIndexTestCase {
|
||||
|
||||
public IndexTestCase(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testIgnoreThisClass() throws Exception {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.data.cpe.xml;
|
||||
|
||||
import java.io.File;
|
||||
import junit.framework.TestCase;
|
||||
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
|
||||
import org.xml.sax.Attributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeremy
|
||||
*/
|
||||
public class ImporterTest extends TestCase {
|
||||
|
||||
public ImporterTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test of all methods within class CPEHandler.
|
||||
* @throws Exception is thrown when an excpetion occurs.
|
||||
*/
|
||||
public void testHandler() throws Exception {
|
||||
System.out.println("importXML");
|
||||
|
||||
File path = new File(this.getClass().getClassLoader().getResource("official-cpe-dictionary_v2.2.xml").getPath());
|
||||
|
||||
Importer.importXML(path.getCanonicalPath());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.reporting;
|
||||
|
||||
import org.codesecure.dependencycheck.scanner.Evidence;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.io.File;
|
||||
import org.codesecure.dependencycheck.scanner.Dependency;
|
||||
import java.util.HashMap;
|
||||
import org.codesecure.dependencycheck.data.BaseIndexTestCase;
|
||||
import java.util.Map;
|
||||
import org.codesecure.dependencycheck.scanner.Evidence.Confidence;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class ReportGeneratorTest extends BaseIndexTestCase {
|
||||
|
||||
public ReportGeneratorTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
@Override
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of generateReport method, of class ReportGenerator.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateReport() throws Exception {
|
||||
System.out.println("generateReport");
|
||||
String templateName = "HtmlReport";
|
||||
File f = new File("target/test-reports");
|
||||
if (!f.exists()) {
|
||||
f.mkdir();
|
||||
}
|
||||
String writeTo = "target/test-reports/Report.html";
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
Dependency d = new Dependency();
|
||||
d.setFileName("FileName.jar");
|
||||
d.setFilePath("lib/FileName.jar");
|
||||
d.addCPEentry("cpe://a:/some:cpe:1.0");
|
||||
|
||||
List<Dependency> dependencies = new ArrayList<Dependency>();
|
||||
d.getTitleEvidence().addEvidence("jar","filename","<test>test", Confidence.HIGH);
|
||||
d.getTitleEvidence().addEvidence("manifest","vendor","<test>test", Confidence.HIGH);
|
||||
|
||||
for (Evidence e : d.getTitleEvidence().iterator(Confidence.HIGH)) {
|
||||
String t = e.getValue();
|
||||
}
|
||||
dependencies.add(d);
|
||||
|
||||
Dependency d2 = new Dependency();
|
||||
d2.setFileName("Another.jar");
|
||||
d2.setFilePath("lib/Another.jar");
|
||||
d2.addCPEentry("cpe://a:/another:cpe:1.0");
|
||||
d2.addCPEentry("cpe://a:/another:cpe:1.1");
|
||||
d2.addCPEentry("cpe://a:/another:cpe:1.2");
|
||||
d2.getTitleEvidence().addEvidence("jar","filename","another.jar", Confidence.HIGH);
|
||||
d2.getTitleEvidence().addEvidence("manifest","vendor","Company A", Confidence.MEDIUM);
|
||||
|
||||
for (Evidence e : d2.getTitleEvidence().iterator(Confidence.HIGH)) {
|
||||
String t = e.getValue();
|
||||
}
|
||||
|
||||
dependencies.add(d2);
|
||||
|
||||
Dependency d3 = new Dependency();
|
||||
d3.setFileName("Third.jar");
|
||||
d3.setFilePath("lib/Third.jar");
|
||||
d3.getTitleEvidence().addEvidence("jar","filename","third.jar", Confidence.HIGH);
|
||||
|
||||
for (Evidence e : d3.getTitleEvidence().iterator(Confidence.HIGH)) {
|
||||
String t = e.getValue();
|
||||
}
|
||||
|
||||
dependencies.add(d3);
|
||||
|
||||
properties.put("dependencies",dependencies);
|
||||
|
||||
ReportGenerator instance = new ReportGenerator();
|
||||
instance.generateReport(templateName, writeTo, properties);
|
||||
//TODO add an assertion here...
|
||||
//assertTrue("need to add a real check here", false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class DependencyTest {
|
||||
|
||||
public DependencyTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of containsUsedString method, of class Dependency.
|
||||
*/
|
||||
@Test
|
||||
public void testContainsUsedString() {
|
||||
System.out.println("containsUsedString");
|
||||
String str = "apache";
|
||||
String str2 = "codesecure";
|
||||
Dependency instance = new Dependency();
|
||||
instance.vendorEvidence.addEvidence("manifest", "something", "apache", Evidence.Confidence.HIGH);
|
||||
instance.vendorEvidence.addEvidence("manifest", "something", "codesecure", Evidence.Confidence.MEDIUM);
|
||||
assertFalse(instance.containsUsedString(str));
|
||||
assertFalse(instance.containsUsedString(str2));
|
||||
for (Evidence i : instance.vendorEvidence.iterator(Evidence.Confidence.HIGH)) {
|
||||
String readValue = i.getValue();
|
||||
}
|
||||
assertTrue(instance.containsUsedString(str));
|
||||
assertFalse(instance.containsUsedString(str2));
|
||||
for (Evidence i : instance.vendorEvidence.iterator(Evidence.Confidence.MEDIUM)) {
|
||||
String readValue = i.getValue();
|
||||
}
|
||||
assertTrue(instance.containsUsedString(str));
|
||||
assertTrue(instance.containsUsedString(str2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
|
||||
import java.io.File;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class JarAnalyzerTest {
|
||||
|
||||
public JarAnalyzerTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of insepct method, of class JarAnalyzer.
|
||||
* @throws Exception is thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testInsepct() throws Exception {
|
||||
System.out.println("insepct");
|
||||
File file = new File(this.getClass().getClassLoader().getResource("struts2-core-2.1.2.jar").getPath());
|
||||
JarAnalyzer instance = new JarAnalyzer();
|
||||
Dependency result = instance.insepct(file);
|
||||
assertEquals("C30B57142E1CCBC1EFD5CD15F307358F", result.getMd5sum());
|
||||
assertEquals("89CE9E36AA9A9E03F1450936D2F4F8DD0F961F8B", result.getSha1sum());
|
||||
assertTrue(result.getVendorEvidence().toString().toLowerCase().contains("apache"));
|
||||
assertTrue(result.getVendorEvidence().getWeighting().contains("apache"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.scanner;
|
||||
|
||||
import org.codesecure.dependencycheck.data.cpe.CPEQuery;
|
||||
import java.io.IOException;
|
||||
import org.codesecure.dependencycheck.data.BaseIndexTestCase;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.codesecure.dependencycheck.reporting.ReportGenerator;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class ScannerTest extends BaseIndexTestCase{
|
||||
|
||||
public ScannerTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test of scan method, of class Scanner.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testScan() throws Exception {
|
||||
System.out.println("scan");
|
||||
String path = "./src/test/resources";
|
||||
Scanner instance = new Scanner();
|
||||
instance.scan(path);
|
||||
assertTrue(instance.getDependencies().size()>0);
|
||||
// CPEQuery query = new CPEQuery();
|
||||
// query.open();
|
||||
// List<Dependency> dependencies = instance.getDependencies();
|
||||
// for (Dependency d : dependencies) {
|
||||
// query.determineCPE(d);
|
||||
// }
|
||||
// query.close();
|
||||
// ReportGenerator rg = new ReportGenerator();
|
||||
// rg.generateReports("./target/", "DependencyCheck", instance.getDependencies());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeremy
|
||||
*/
|
||||
public class ChecksumTest extends TestCase {
|
||||
|
||||
public ChecksumTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getChecksum method, of class Checksum.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testGetChecksum() throws Exception {
|
||||
System.out.println("getChecksum (md5)");
|
||||
String algorithm = "MD5";
|
||||
File file = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").getPath());
|
||||
byte[] expResult = {-16, -111, 92, 95, 70, -72, -49, -94, -125, -27, -83, 103, -96, -101, 55, -109};
|
||||
byte[] result = Checksum.getChecksum(algorithm, file);
|
||||
boolean arraysAreEqual = true;
|
||||
if (expResult.length == result.length) {
|
||||
for (int i = 0; arraysAreEqual && i < result.length; i++) {
|
||||
arraysAreEqual = result[i] == expResult[i];
|
||||
}
|
||||
} else {
|
||||
fail("Checksum results do not match expected results.");
|
||||
}
|
||||
assertTrue(arraysAreEqual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getChecksum method, of class Checksum. This checks that an
|
||||
* excpetion is thrown when an invalid path is specified.
|
||||
*
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testGetChecksum_FileNotFound() throws Exception {
|
||||
System.out.println("getChecksum (invalid path)");
|
||||
String algorithm = "MD5";
|
||||
File file = new File("not a valid file");
|
||||
boolean exceptionThrown = false;
|
||||
try {
|
||||
byte[] result = Checksum.getChecksum(algorithm, file);
|
||||
} catch (FileNotFoundException ex) {
|
||||
exceptionThrown = true;
|
||||
}
|
||||
assertTrue(exceptionThrown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getChecksum method, of class Checksum. This checks that an
|
||||
* exception is thrown when an invalid algorithm is specified.
|
||||
*
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testGetChecksum_NoSuchAlgorithm() throws Exception {
|
||||
System.out.println("getChecksum (invalid algorithm)");
|
||||
String algorithm = "some unknown algorithm";
|
||||
File file = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").getPath());
|
||||
boolean exceptionThrown = false;
|
||||
try {
|
||||
byte[] result = Checksum.getChecksum(algorithm, file);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
exceptionThrown = true;
|
||||
}
|
||||
assertTrue(exceptionThrown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getMD5Checksum method, of class Checksum.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testGetMD5Checksum() throws Exception {
|
||||
System.out.println("getMD5Checksum");
|
||||
File file = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").getPath());
|
||||
String expResult = "F0915C5F46B8CFA283E5AD67A09B3793";
|
||||
String result = Checksum.getMD5Checksum(file);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getSHA1Checksum method, of class Checksum.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testGetSHA1Checksum() throws Exception {
|
||||
System.out.println("getSHA1Checksum");
|
||||
File file = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").getPath());
|
||||
String expResult = "B8A9FF28B21BCB1D0B50E24A5243D8B51766851A";
|
||||
String result = Checksum.getSHA1Checksum(file);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getHex method, of class Checksum.
|
||||
*/
|
||||
@Test
|
||||
public void testGetHex() {
|
||||
System.out.println("getHex");
|
||||
byte[] raw = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
||||
String expResult = "000102030405060708090A0B0C0D0E0F10";
|
||||
String result = Checksum.getHex(raw);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeremy
|
||||
*/
|
||||
public class CliParserTest extends TestCase {
|
||||
|
||||
public CliParserTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse() throws Exception {
|
||||
System.out.println("parse");
|
||||
|
||||
String[] args = {};
|
||||
PrintStream out = System.out;
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(baos));
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
instance.parse(args);
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with help arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_help() throws Exception {
|
||||
System.out.println("parse -help");
|
||||
|
||||
String[] args = {"-help"};
|
||||
PrintStream out = System.out;
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
instance.parse(args);
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertTrue(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with version arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_version() throws Exception {
|
||||
System.out.println("parse -ver");
|
||||
|
||||
String[] args = {"-version"};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
instance.parse(args);
|
||||
assertTrue(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with jar and cpe args, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_scan_cpe() throws Exception {
|
||||
System.out.println("parse -cpe file -scan file");
|
||||
|
||||
String[] args = {"-scan", "file", "-cpe", "file"};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
try {
|
||||
instance.parse(args);
|
||||
} catch (ParseException ex) {
|
||||
assertTrue(ex.getMessage().contains("an option from this group has already been selected"));
|
||||
}
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with jar and cpe args, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_unknown() throws Exception {
|
||||
System.out.println("parse -unknown");
|
||||
|
||||
String[] args = {"-unknown"};
|
||||
|
||||
PrintStream out = System.out;
|
||||
PrintStream err = System.err;
|
||||
ByteArrayOutputStream baos_out = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream baos_err = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(baos_out));
|
||||
System.setErr(new PrintStream(baos_err));
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
|
||||
try {
|
||||
instance.parse(args);
|
||||
} catch (ParseException ex) {
|
||||
assertTrue(ex.getMessage().contains("Unrecognized option"));
|
||||
}
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with scan arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_scan() throws Exception {
|
||||
System.out.println("parse -scan");
|
||||
|
||||
String[] args = {"-scan"};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
|
||||
try {
|
||||
instance.parse(args);
|
||||
} catch (ParseException ex) {
|
||||
assertTrue(ex.getMessage().contains("Missing argument"));
|
||||
}
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with jar arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_scan_unknownFile() throws Exception {
|
||||
System.out.println("parse -scan jar.that.does.not.exist");
|
||||
|
||||
String[] args = {"-scan", "jar.that.does.not.exist", "-app", "test"};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
try {
|
||||
instance.parse(args);
|
||||
} catch (FileNotFoundException ex) {
|
||||
assertTrue(ex.getMessage().contains("Invalid file argument"));
|
||||
}
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with jar arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_scan_withFileExists() throws Exception {
|
||||
System.out.println("parse -scan checkSumTest.file");
|
||||
File path = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").getPath());
|
||||
String[] args = {"-scan", path.getCanonicalPath(), "-out", "./", "-app", "test"};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
instance.parse(args);
|
||||
|
||||
assertEquals(path.getCanonicalPath(), instance.getScanFiles()[0]);
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertTrue(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with cpe arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_cpe() throws Exception {
|
||||
System.out.println("parse -cpe");
|
||||
|
||||
String[] args = {"-cpe"};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
|
||||
try {
|
||||
instance.parse(args);
|
||||
} catch (ParseException ex) {
|
||||
assertTrue(ex.getMessage().contains("Missing argument"));
|
||||
}
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with jar arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_cpe_unknownFile() throws Exception {
|
||||
System.out.println("parse -cpe cpe.that.does.not.exist");
|
||||
|
||||
String[] args = {"-cpe", "cpe.that.does.not.exist"};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
try {
|
||||
instance.parse(args);
|
||||
} catch (FileNotFoundException ex) {
|
||||
assertTrue(ex.getMessage().contains("Invalid file argument"));
|
||||
}
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertFalse(instance.isLoadCPE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of parse method with jar arg, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_cpe_withFileExists() throws Exception {
|
||||
System.out.println("parse -cpe checkSumTest.file");
|
||||
File path = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").getPath());
|
||||
String[] args = {"-cpe", path.getCanonicalPath()};
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
instance.parse(args);
|
||||
|
||||
assertEquals(path.getCanonicalPath(), instance.getCpeFile());
|
||||
|
||||
assertFalse(instance.isGetVersion());
|
||||
assertFalse(instance.isGetHelp());
|
||||
assertFalse(instance.isRunScan());
|
||||
assertTrue(instance.isLoadCPE());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of printVersionInfo, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_printVersionInfo() throws Exception {
|
||||
System.out.println("printVersionInfo");
|
||||
|
||||
PrintStream out = System.out;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(baos));
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
instance.printVersionInfo();
|
||||
try {
|
||||
baos.flush();
|
||||
String text = (new String(baos.toByteArray())).toLowerCase();
|
||||
String[] lines = text.split(System.getProperty("line.separator"));
|
||||
assertEquals(1, lines.length);
|
||||
assertTrue(text.contains("version"));
|
||||
assertTrue(!text.contains("unknown"));
|
||||
} catch (IOException ex) {
|
||||
System.setOut(out);
|
||||
fail("CliParser.printVersionInfo did not write anything to system.out.");
|
||||
} finally {
|
||||
System.setOut(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of printHelp, of class CliParser.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParse_printHelp() throws Exception {
|
||||
System.out.println("printHelp");
|
||||
|
||||
PrintStream out = System.out;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(baos));
|
||||
|
||||
CliParser instance = new CliParser();
|
||||
instance.printHelp();
|
||||
try {
|
||||
baos.flush();
|
||||
String text = (new String(baos.toByteArray()));
|
||||
String[] lines = text.split(System.getProperty("line.separator"));
|
||||
assertEquals("usage: DependencyCheck [-a <name>] [-c <file> | -s <path>] [-h] [-o", lines[0]);
|
||||
assertEquals(" <folder>] [-v]", lines[1]);
|
||||
assertEquals(8, lines.length);
|
||||
} catch (IOException ex) {
|
||||
System.setOut(out);
|
||||
fail("CliParser.printVersionInfo did not write anything to system.out.");
|
||||
} finally {
|
||||
System.setOut(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
import java.net.URL;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class DownloaderTest {
|
||||
|
||||
public DownloaderTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of fetchFile method, of class Downloader.
|
||||
* @throws Exception thrown when an excpetion occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testFetchFile_URL_String() throws Exception {
|
||||
System.out.println("fetchFile");
|
||||
URL url = new URL(Settings.getString(Settings.KEYS.CPE_URL));
|
||||
String outputPath = "target\\downloaded_cpe.xml";
|
||||
Downloader.fetchFile(url, outputPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class FilterTest {
|
||||
|
||||
public FilterTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of passes method, of class Filter.
|
||||
*/
|
||||
@Test
|
||||
public void testPasses() {
|
||||
System.out.println("passes");
|
||||
String keep = "keep";
|
||||
String fail = "fail";
|
||||
|
||||
assertTrue("String contained keep - but passes returned false.", TEST_FILTER.passes(keep));
|
||||
assertFalse("String contained fail - but passes returned true.", TEST_FILTER.passes(fail));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of filter method, of class Filter.
|
||||
*/
|
||||
@Test
|
||||
public void testFilter_Iterable() {
|
||||
System.out.println("filter");
|
||||
List<String> testData = new ArrayList<String>();
|
||||
testData.add("keep");
|
||||
testData.add("remove");
|
||||
testData.add("keep");
|
||||
|
||||
List<String> expResults = new ArrayList<String>();
|
||||
expResults.add("keep");
|
||||
expResults.add("keep");
|
||||
|
||||
List<String> actResults = new ArrayList<String>();
|
||||
for (String s : TEST_FILTER.filter(testData)) {
|
||||
actResults.add(s);
|
||||
}
|
||||
assertArrayEquals(expResults.toArray(), actResults.toArray());
|
||||
}
|
||||
private static final Filter<String> TEST_FILTER =
|
||||
new Filter<String>() {
|
||||
|
||||
public boolean passes(String str) {
|
||||
return str.contains("keep");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.utils;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeremy
|
||||
*/
|
||||
public class SettingsTest extends TestCase {
|
||||
|
||||
public SettingsTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getString method, of class Settings.
|
||||
*/
|
||||
@Test
|
||||
public void testGetString() {
|
||||
System.out.println("getString");
|
||||
String key = Settings.KEYS.CPE_INDEX;
|
||||
String expResult = "target/store/cpe";
|
||||
String result = Settings.getString(key);
|
||||
assertTrue(result.endsWith(expResult));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.cpe.xml;
|
||||
|
||||
import org.codesecure.dependencycheck.data.cpe.Entry;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class CPEEntryTest extends TestCase {
|
||||
|
||||
public CPEEntryTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setName method, of class Entry.
|
||||
*/
|
||||
public void testSetName() throws Exception {
|
||||
System.out.println("setName");
|
||||
String name = "cpe:/a:apache:struts:1.1:rc2";
|
||||
|
||||
Entry instance = new Entry();
|
||||
instance.setName(name);
|
||||
|
||||
assertEquals(name,instance.getName());
|
||||
assertEquals("apache", instance.getVendor());
|
||||
assertEquals("struts", instance.getProduct());
|
||||
assertEquals("1.1", instance.getVersion());
|
||||
assertEquals("rc2", instance.getRevision());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.codesecure.dependencycheck.cpe.xml;
|
||||
|
||||
import java.io.File;
|
||||
import junit.framework.TestCase;
|
||||
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
|
||||
import org.xml.sax.Attributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeremy
|
||||
*/
|
||||
public class CPEHandlerTest extends TestCase {
|
||||
|
||||
public CPEHandlerTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test of all methods within class CPEHandler.
|
||||
*/
|
||||
public void testHandler() throws Exception {
|
||||
System.out.println("CPEHandler");
|
||||
|
||||
File path = new File(this.getClass().getClassLoader().getResource("official-cpe-dictionary_v2.2.xml").getPath());
|
||||
|
||||
|
||||
Importer.importXML(path.getCanonicalPath());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
1
src/test/resources/checkSumTest.file
Normal file
1
src/test/resources/checkSumTest.file
Normal file
@@ -0,0 +1 @@
|
||||
this is a test file used to check the checksums.
|
||||
BIN
src/test/resources/commons-cli-1.2.jar
Normal file
BIN
src/test/resources/commons-cli-1.2.jar
Normal file
Binary file not shown.
BIN
src/test/resources/jetty-6.1.0.jar
Normal file
BIN
src/test/resources/jetty-6.1.0.jar
Normal file
Binary file not shown.
160343
src/test/resources/official-cpe-dictionary_v2.2.xml
Normal file
160343
src/test/resources/official-cpe-dictionary_v2.2.xml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/test/resources/org.mortbay.jetty.jar
Normal file
BIN
src/test/resources/org.mortbay.jetty.jar
Normal file
Binary file not shown.
BIN
src/test/resources/org.mortbay.jmx.jar
Normal file
BIN
src/test/resources/org.mortbay.jmx.jar
Normal file
Binary file not shown.
BIN
src/test/resources/struts2-core-2.1.2.jar
Normal file
BIN
src/test/resources/struts2-core-2.1.2.jar
Normal file
Binary file not shown.
Reference in New Issue
Block a user