initial
This commit is contained in:
+203
@@ -0,0 +1,203 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.7.8] - 2026-02-23
|
||||
### Changed
|
||||
- Update dependencies. [#44736]
|
||||
|
||||
## [0.7.7] - 2026-02-02
|
||||
### Changed
|
||||
- Update dependencies. [#44736]
|
||||
|
||||
## [0.7.6] - 2025-12-08
|
||||
### Fixed
|
||||
- Ensure proper flags are used with `json_encode()`. [#46092]
|
||||
|
||||
## [0.7.5] - 2025-12-01
|
||||
### Changed
|
||||
- Update dependencies. [#44736]
|
||||
|
||||
## [0.7.4] - 2025-10-20
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.7.3] - 2025-09-15
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.7.2] - 2025-08-11
|
||||
### Changed
|
||||
- Update dependencies. [#44673]
|
||||
|
||||
## [0.7.1] - 2025-08-04
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.7.0] - 2025-07-30
|
||||
### Added
|
||||
- Ensure user is fully aware that deleting an extension via delete-fixer may break their site. [#44521]
|
||||
|
||||
## [0.6.1] - 2025-07-21
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [0.6.0] - 2025-06-05
|
||||
### Added
|
||||
- Add functionality to correctly display database threats in the Protect UI. [#43663]
|
||||
|
||||
## [0.5.11] - 2025-05-05
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [0.5.10] - 2025-04-28
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.5.9] - 2025-03-31
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [0.5.8] - 2025-03-21
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.5.7] - 2025-03-18
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [0.5.6] - 2025-03-17
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.5.5] - 2025-03-12
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.5.4] - 2025-03-05
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.5.3] - 2025-03-03
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.5.2] - 2025-02-24
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [0.5.1] - 2025-02-11
|
||||
### Fixed
|
||||
- Protect Status: Ensure vulnerabilities property is always an array. [#41694]
|
||||
|
||||
## [0.5.0] - 2025-02-10
|
||||
### Changed
|
||||
- Combine multiple vulnerability results for the same extension into a single vulnerable extension threat result. [#40863]
|
||||
|
||||
## [0.4.3] - 2025-02-03
|
||||
### Fixed
|
||||
- Code: Remove extra params on function calls. [#41263]
|
||||
- Fix a bug when core version data is not interpreted correctly from the report data response. [#41503]
|
||||
|
||||
## [0.4.2] - 2025-01-20
|
||||
### Fixed
|
||||
- Fix Current_Plan::supports() call from breaking cache on every call. [#41010]
|
||||
|
||||
## [0.4.1] - 2024-12-23
|
||||
### Fixed
|
||||
- Fix PHP warnings caused by uninstalled extensions. [#40622]
|
||||
|
||||
## [0.4.0] - 2024-12-04
|
||||
### Added
|
||||
- Add extension data to threats. [#40400]
|
||||
|
||||
## [0.3.1] - 2024-11-25
|
||||
### Changed
|
||||
- Updated dependencies. [#40286]
|
||||
|
||||
## [0.3.0] - 2024-11-14
|
||||
### Added
|
||||
- Added threats property to protect status. [#40097]
|
||||
|
||||
### Removed
|
||||
- General: Update minimum PHP version to 7.2. [#40147]
|
||||
|
||||
## [0.2.2] - 2024-11-04
|
||||
### Added
|
||||
- Enable test coverage. [#39961]
|
||||
|
||||
## [0.2.1] - 2024-10-29
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.2.0] - 2024-09-23
|
||||
### Changed
|
||||
- Adds a fixable_threats status property [#39125]
|
||||
|
||||
## [0.1.5] - 2024-09-05
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [0.1.4] - 2024-09-05
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [0.1.3] - 2024-08-26
|
||||
### Changed
|
||||
- Updated package dependencies. [#39004]
|
||||
|
||||
## [0.1.2] - 2024-08-19
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [0.1.1] - 2024-08-09
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## 0.1.0 - 2024-07-15
|
||||
### Added
|
||||
- Initial version. [#37894]
|
||||
|
||||
### Changed
|
||||
- Updated package dependencies. [#37894]
|
||||
|
||||
[0.7.8]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.7...v0.7.8
|
||||
[0.7.7]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.6...v0.7.7
|
||||
[0.7.6]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.5...v0.7.6
|
||||
[0.7.5]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.4...v0.7.5
|
||||
[0.7.4]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.3...v0.7.4
|
||||
[0.7.3]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.2...v0.7.3
|
||||
[0.7.2]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.1...v0.7.2
|
||||
[0.7.1]: https://github.com/Automattic/jetpack-protect-status/compare/v0.7.0...v0.7.1
|
||||
[0.7.0]: https://github.com/Automattic/jetpack-protect-status/compare/v0.6.1...v0.7.0
|
||||
[0.6.1]: https://github.com/Automattic/jetpack-protect-status/compare/v0.6.0...v0.6.1
|
||||
[0.6.0]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.11...v0.6.0
|
||||
[0.5.11]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.10...v0.5.11
|
||||
[0.5.10]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.9...v0.5.10
|
||||
[0.5.9]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.8...v0.5.9
|
||||
[0.5.8]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.7...v0.5.8
|
||||
[0.5.7]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.6...v0.5.7
|
||||
[0.5.6]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.5...v0.5.6
|
||||
[0.5.5]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.4...v0.5.5
|
||||
[0.5.4]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.3...v0.5.4
|
||||
[0.5.3]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.2...v0.5.3
|
||||
[0.5.2]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.1...v0.5.2
|
||||
[0.5.1]: https://github.com/Automattic/jetpack-protect-status/compare/v0.5.0...v0.5.1
|
||||
[0.5.0]: https://github.com/Automattic/jetpack-protect-status/compare/v0.4.3...v0.5.0
|
||||
[0.4.3]: https://github.com/Automattic/jetpack-protect-status/compare/v0.4.2...v0.4.3
|
||||
[0.4.2]: https://github.com/Automattic/jetpack-protect-status/compare/v0.4.1...v0.4.2
|
||||
[0.4.1]: https://github.com/Automattic/jetpack-protect-status/compare/v0.4.0...v0.4.1
|
||||
[0.4.0]: https://github.com/Automattic/jetpack-protect-status/compare/v0.3.1...v0.4.0
|
||||
[0.3.1]: https://github.com/Automattic/jetpack-protect-status/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/Automattic/jetpack-protect-status/compare/v0.2.2...v0.3.0
|
||||
[0.2.2]: https://github.com/Automattic/jetpack-protect-status/compare/v0.2.1...v0.2.2
|
||||
[0.2.1]: https://github.com/Automattic/jetpack-protect-status/compare/v0.2.0...v0.2.1
|
||||
[0.2.0]: https://github.com/Automattic/jetpack-protect-status/compare/v0.1.5...v0.2.0
|
||||
[0.1.5]: https://github.com/Automattic/jetpack-protect-status/compare/v0.1.4...v0.1.5
|
||||
[0.1.4]: https://github.com/Automattic/jetpack-protect-status/compare/v0.1.3...v0.1.4
|
||||
[0.1.3]: https://github.com/Automattic/jetpack-protect-status/compare/v0.1.2...v0.1.3
|
||||
[0.1.2]: https://github.com/Automattic/jetpack-protect-status/compare/v0.1.1...v0.1.2
|
||||
[0.1.1]: https://github.com/Automattic/jetpack-protect-status/compare/v0.1.0...v0.1.1
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
===================================
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) 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
|
||||
this service 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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. 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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
|
||||
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
|
||||
convey 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 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision 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, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Moe Ghoul>, 1 April 1989
|
||||
Moe Ghoul, President of Vice
|
||||
|
||||
This 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.
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# Security Policy
|
||||
|
||||
Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Generally, only the latest version of Jetpack and its associated plugins have continued support. If a critical vulnerability is found in the current version of a plugin, we may opt to backport any patches to previous versions.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Our HackerOne program covers the below plugin software, as well as a variety of related projects and infrastructure:
|
||||
|
||||
* [Jetpack](https://jetpack.com/)
|
||||
* Jetpack Backup
|
||||
* Jetpack Boost
|
||||
* Jetpack CRM
|
||||
* Jetpack Protect
|
||||
* Jetpack Search
|
||||
* Jetpack Social
|
||||
* Jetpack VideoPress
|
||||
|
||||
**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
|
||||
|
||||
Our most critical targets are:
|
||||
|
||||
* Jetpack and the Jetpack composer packages (all within this repo)
|
||||
* Jetpack.com -- the primary marketing site.
|
||||
* cloud.jetpack.com -- a management site.
|
||||
* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
|
||||
|
||||
For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
|
||||
|
||||
_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
|
||||
|
||||
## Guidelines
|
||||
|
||||
We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
|
||||
|
||||
* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
|
||||
* Pen-testing Production:
|
||||
* Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
|
||||
* If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
|
||||
* **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
|
||||
* To be eligible for a bounty, all of these guidelines must be followed.
|
||||
* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
|
||||
|
||||
We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "automattic/jetpack-protect-status",
|
||||
"description": "This package contains the Protect Status API functionality to retrieve a site's scan status (WordPress, Themes, and Plugins threats).",
|
||||
"type": "jetpack-library",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"automattic/jetpack-connection": "^8.0.0",
|
||||
"automattic/jetpack-plugins-installer": "^0.5.7",
|
||||
"automattic/jetpack-sync": "^4.29.0",
|
||||
"automattic/jetpack-protect-models": "^0.6.1",
|
||||
"automattic/jetpack-plans": "^0.11.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"yoast/phpunit-polyfills": "^4.0.0",
|
||||
"automattic/jetpack-changelogger": "^6.0.14",
|
||||
"automattic/jetpack-test-environment": "@dev",
|
||||
"automattic/phpunit-select-config": "^1.0.3"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"build-development": "echo 'Add your build step to composer.json, please!'",
|
||||
"build-production": "echo 'Add your build step to composer.json, please!'",
|
||||
"phpunit": [
|
||||
"phpunit-select-config phpunit.#.xml.dist --colors=always"
|
||||
],
|
||||
"test-coverage": [
|
||||
"php -dpcov.directory=. ./vendor/bin/phpunit-select-config phpunit.#.xml.dist --coverage-php \"$COVERAGE_DIR/php.cov\""
|
||||
],
|
||||
"test-php": [
|
||||
"@composer phpunit"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"roots/wordpress-core-installer": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"autotagger": true,
|
||||
"branch-alias": {
|
||||
"dev-trunk": "0.7.x-dev"
|
||||
},
|
||||
"changelogger": {
|
||||
"link-template": "https://github.com/Automattic/jetpack-protect-status/compare/v${old}...v${new}"
|
||||
},
|
||||
"mirror-repo": "Automattic/jetpack-protect-status",
|
||||
"textdomain": "jetpack-protect-status",
|
||||
"version-constants": {
|
||||
"::PACKAGE_VERSION": "src/class-status.php"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to handle the Protect plan
|
||||
*
|
||||
* @package automattic/jetpack-protect-status
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Protect_Status;
|
||||
|
||||
use Automattic\Jetpack\Current_Plan;
|
||||
|
||||
/**
|
||||
* The Plan class.
|
||||
*/
|
||||
class Plan {
|
||||
/**
|
||||
* The meta name used to store the cache date
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CACHE_DATE_META_NAME = 'protect-cache-date';
|
||||
|
||||
/**
|
||||
* Valid pediord for the cache: One week.
|
||||
*/
|
||||
const CACHE_VALIDITY_PERIOD = 7 * DAY_IN_SECONDS;
|
||||
|
||||
/**
|
||||
* The meta name used to store the cache
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CACHE_META_NAME = 'protect-cache';
|
||||
|
||||
/**
|
||||
* Checks if the cache is old, meaning we need to fetch new data from WPCOM
|
||||
*/
|
||||
private static function is_cache_old() {
|
||||
if ( empty( self::get_product_from_cache() ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$cache_date = get_user_meta( get_current_user_id(), self::CACHE_DATE_META_NAME, true );
|
||||
return time() - (int) $cache_date > ( self::CACHE_VALIDITY_PERIOD );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the product list from the user cache
|
||||
*/
|
||||
private static function get_product_from_cache() {
|
||||
return get_user_meta( get_current_user_id(), self::CACHE_META_NAME, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the product data
|
||||
*
|
||||
* @param string $wpcom_product The product slug.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_product( $wpcom_product = 'jetpack_scan' ) {
|
||||
if ( ! self::is_cache_old() ) {
|
||||
return self::get_product_from_cache();
|
||||
}
|
||||
|
||||
$request_url = 'https://public-api.wordpress.com/rest/v1.1/products?locale=' . get_user_locale() . '&type=jetpack';
|
||||
$wpcom_request = wp_remote_get( esc_url_raw( $request_url ) );
|
||||
$response_code = wp_remote_retrieve_response_code( $wpcom_request );
|
||||
|
||||
if ( 200 === $response_code ) {
|
||||
$products = json_decode( wp_remote_retrieve_body( $wpcom_request ) );
|
||||
|
||||
// Pick the desired product...
|
||||
$product = $products->{$wpcom_product};
|
||||
|
||||
// ... and store it into the cache.
|
||||
update_user_meta( get_current_user_id(), self::CACHE_DATE_META_NAME, time() );
|
||||
update_user_meta( get_current_user_id(), self::CACHE_META_NAME, $product );
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
return new \WP_Error(
|
||||
'failed_to_fetch_data',
|
||||
esc_html__( 'Unable to fetch the requested data.', 'jetpack-protect-status' ),
|
||||
array(
|
||||
'status' => $response_code,
|
||||
'request' => $wpcom_request,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Has Required Plan
|
||||
*
|
||||
* @param bool $force_refresh Refresh the local plan cache from wpcom.
|
||||
* @return bool True when the site has a plan or product that supports the paid Protect tier.
|
||||
*/
|
||||
public static function has_required_plan( $force_refresh = false ) {
|
||||
static $has_scan = null;
|
||||
if ( null === $has_scan || $force_refresh ) {
|
||||
$products = array_column( Current_Plan::get_products(), 'product_slug' );
|
||||
|
||||
// Check for a plan or product that enables scan.
|
||||
$plan_supports_scan = Current_Plan::supports( 'scan', $force_refresh );
|
||||
$has_scan_product = count( array_intersect( array( 'jetpack_scan', 'jetpack_scan_monthly' ), $products ) ) > 0;
|
||||
$has_scan = $plan_supports_scan || $has_scan_product;
|
||||
}
|
||||
|
||||
return $has_scan;
|
||||
}
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to handle the Protect Status of Jetpack Protect
|
||||
*
|
||||
* @phan-suppress PhanDeprecatedProperty -- Maintaining backwards compatibility.
|
||||
*
|
||||
* @package automattic/jetpack-protect-status
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Protect_Status;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Plugins_Installer;
|
||||
use Automattic\Jetpack\Protect_Models\Extension_Model;
|
||||
use Automattic\Jetpack\Protect_Models\Status_Model;
|
||||
use Automattic\Jetpack\Protect_Models\Threat_Model;
|
||||
use Automattic\Jetpack\Protect_Models\Vulnerability_Model;
|
||||
use Automattic\Jetpack\Sync\Functions as Sync_Functions;
|
||||
use Jetpack_Options;
|
||||
use WP_Error;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that handles fetching and caching the Status of vulnerabilities check from the WPCOM servers
|
||||
*/
|
||||
class Protect_Status extends Status {
|
||||
|
||||
/**
|
||||
* WPCOM endpoint
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const REST_API_BASE = '/sites/%d/jetpack-protect-status';
|
||||
|
||||
/**
|
||||
* Name of the option where status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_NAME = 'jetpack_protect_status';
|
||||
|
||||
/**
|
||||
* Name of the option where the timestamp of the status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TIMESTAMP_NAME = 'jetpack_protect_status_time';
|
||||
|
||||
/**
|
||||
* Gets the current status of the Jetpack Protect checks
|
||||
*
|
||||
* @param bool $refresh_from_wpcom Refresh the local plan and status cache from wpcom.
|
||||
* @return Status_Model
|
||||
*/
|
||||
public static function get_status( $refresh_from_wpcom = false ) {
|
||||
if ( self::$status !== null ) {
|
||||
return self::$status;
|
||||
}
|
||||
|
||||
if ( $refresh_from_wpcom || ! self::should_use_cache() || self::is_cache_expired() ) {
|
||||
$status = self::fetch_from_server();
|
||||
} else {
|
||||
$status = self::get_from_options();
|
||||
}
|
||||
|
||||
if ( is_wp_error( $status ) ) {
|
||||
$status = new Status_Model(
|
||||
array(
|
||||
'error' => true,
|
||||
'error_code' => $status->get_error_code(),
|
||||
'error_message' => $status->get_error_message(),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$status = self::normalize_protect_report_data( $status );
|
||||
}
|
||||
|
||||
self::$status = $status;
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the WPCOM API endpoint
|
||||
*
|
||||
* @return WP_Error|string
|
||||
*/
|
||||
public static function get_api_url() {
|
||||
$blog_id = Jetpack_Options::get_option( 'id' );
|
||||
$is_connected = ( new Connection_Manager() )->is_connected();
|
||||
|
||||
if ( ! $blog_id || ! $is_connected ) {
|
||||
return new WP_Error( 'site_not_connected' );
|
||||
}
|
||||
|
||||
$api_url = sprintf( self::REST_API_BASE, $blog_id );
|
||||
|
||||
return $api_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the status from WPCOM servers
|
||||
*
|
||||
* @return WP_Error|array
|
||||
*/
|
||||
public static function fetch_from_server() {
|
||||
$api_url = self::get_api_url();
|
||||
if ( is_wp_error( $api_url ) ) {
|
||||
return $api_url;
|
||||
}
|
||||
|
||||
$response = Client::wpcom_json_api_request_as_blog(
|
||||
self::get_api_url(),
|
||||
'2',
|
||||
array(
|
||||
'method' => 'GET',
|
||||
'timeout' => 30,
|
||||
),
|
||||
null,
|
||||
'wpcom'
|
||||
);
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( is_wp_error( $response ) || 200 !== $response_code || empty( $response['body'] ) ) {
|
||||
return new WP_Error( 'failed_fetching_status', 'Failed to fetch Protect Status data from server', array( 'status' => $response_code ) );
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
self::update_status_option( $body );
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize data from the Protect Report data source.
|
||||
*
|
||||
* @phan-suppress PhanDeprecatedProperty -- Maintaining backwards compatibility.
|
||||
*
|
||||
* @param object $report_data Data from the Protect Report.
|
||||
* @return Status_Model
|
||||
*/
|
||||
protected static function normalize_protect_report_data( $report_data ) {
|
||||
$status = new Status_Model();
|
||||
$status->data_source = 'protect_report';
|
||||
|
||||
// map report data properties directly into the Status_Model
|
||||
$status->status = isset( $report_data->status ) ? $report_data->status : null;
|
||||
$status->last_checked = isset( $report_data->last_checked ) ? $report_data->last_checked : null;
|
||||
$status->num_threats = isset( $report_data->num_vulnerabilities ) ? $report_data->num_vulnerabilities : null;
|
||||
$status->num_themes_threats = isset( $report_data->num_themes_vulnerabilities ) ? $report_data->num_themes_vulnerabilities : null;
|
||||
$status->num_plugins_threats = isset( $report_data->num_plugins_vulnerabilities ) ? $report_data->num_plugins_vulnerabilities : null;
|
||||
$status->has_unchecked_items = false;
|
||||
|
||||
// normalize extension information
|
||||
self::normalize_extension_data( $status, $report_data, 'themes' );
|
||||
self::normalize_extension_data( $status, $report_data, 'plugins' );
|
||||
self::normalize_core_data( $status, $report_data );
|
||||
|
||||
// sort extensions by number of threats
|
||||
$status->themes = self::sort_threats( $status->themes );
|
||||
$status->plugins = self::sort_threats( $status->plugins );
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize theme and plugin information from the Protect Report data source.
|
||||
*
|
||||
* @phan-suppress PhanDeprecatedProperty -- Maintaining backwards compatibility.
|
||||
*
|
||||
* @param object $status The status object to normalize.
|
||||
* @param object $report_data Data from the Protect Report.
|
||||
* @param string $extension_type The type of extension to normalize. Either 'themes' or 'plugins'.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function normalize_extension_data( &$status, $report_data, $extension_type ) {
|
||||
if ( ! in_array( $extension_type, array( 'plugins', 'themes' ), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$installed_extensions = 'plugins' === $extension_type ? Plugins_Installer::get_plugins() : Sync_Functions::get_themes();
|
||||
$checked_extensions = isset( $report_data->{ $extension_type } ) ? $report_data->{ $extension_type } : new \stdClass();
|
||||
|
||||
/**
|
||||
* Extension slug <=> threats data map.
|
||||
*
|
||||
* @var Extension_Model[] $extension_threats Array of Extension_Model objects indexed by slug.
|
||||
*/
|
||||
$extension_threats = array();
|
||||
|
||||
// Initialize the extension threats map with all extensions currently installed on the site
|
||||
foreach ( $installed_extensions as $slug => $installed_extension ) {
|
||||
$extension_threats[ $slug ] = new Extension_Model(
|
||||
array(
|
||||
'slug' => $slug,
|
||||
'name' => $installed_extension['Name'],
|
||||
'version' => $installed_extension['Version'],
|
||||
'type' => $extension_type,
|
||||
'checked' => isset( $checked_extensions->{ $slug } ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $checked_extensions as $slug => $checked_extension ) {
|
||||
$installed_extension = $installed_extensions[ $slug ] ?? null;
|
||||
|
||||
// extension is no longer installed on the site
|
||||
if ( ! $installed_extension ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$extension = new Extension_Model(
|
||||
array(
|
||||
'name' => $installed_extension['Name'],
|
||||
'version' => $installed_extension['Version'],
|
||||
'slug' => $slug,
|
||||
'checked' => false,
|
||||
'type' => $extension_type,
|
||||
)
|
||||
);
|
||||
|
||||
// extension version has changed since the report
|
||||
if ( $installed_extension['Version'] !== $checked_extension->version ) {
|
||||
// maintain $status->{ themes|plugins } for backwards compatibility.
|
||||
$extension_threats[ $slug ] = $extension;
|
||||
continue;
|
||||
}
|
||||
|
||||
$extension->checked = true;
|
||||
$extension_threats[ $slug ] = $extension;
|
||||
|
||||
if ( is_array( $checked_extension->vulnerabilities ) && ! empty( $checked_extension->vulnerabilities ) ) {
|
||||
// normalize the vulnerabilities data
|
||||
$vulnerabilities = array_map(
|
||||
function ( $vulnerability ) {
|
||||
return new Vulnerability_Model( $vulnerability );
|
||||
},
|
||||
$checked_extension->vulnerabilities
|
||||
);
|
||||
|
||||
// convert the detected vulnerabilities into a vulnerable extension threat
|
||||
$threat = Threat_Model::generate_from_extension_vulnerabilities( $extension, $vulnerabilities );
|
||||
|
||||
$threat_extension = clone $extension;
|
||||
$extension_threat = clone $threat;
|
||||
|
||||
$extension_threat->extension = null;
|
||||
$extension_threats[ $slug ]->threats[] = $extension_threat;
|
||||
|
||||
$threat->extension = $threat_extension;
|
||||
$status->threats[] = $threat;
|
||||
}
|
||||
}
|
||||
|
||||
$status->{ $extension_type } = array_values( $extension_threats );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the core information from the Protect Report data source.
|
||||
*
|
||||
* @phan-suppress PhanDeprecatedProperty -- Maintaining backwards compatibility.
|
||||
*
|
||||
* @param object $status The status object to normalize.
|
||||
* @param object $report_data Data from the Protect Report.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function normalize_core_data( &$status, $report_data ) {
|
||||
global $wp_version;
|
||||
|
||||
// Ensure the report data has the core property.
|
||||
if ( ! isset( $report_data->core ) || ! $report_data->core
|
||||
|| ! isset( $report_data->core->version ) || ! $report_data->core->version ) {
|
||||
$report_data->core = new \stdClass();
|
||||
$report_data->core->version = new \stdClass();
|
||||
}
|
||||
|
||||
$core = new Extension_Model(
|
||||
array(
|
||||
'type' => 'core',
|
||||
'name' => 'WordPress',
|
||||
'slug' => 'wordpress',
|
||||
'version' => $wp_version,
|
||||
'checked' => false,
|
||||
)
|
||||
);
|
||||
|
||||
// Core version has changed since the report.
|
||||
if ( $report_data->core->version !== $wp_version ) {
|
||||
// Maintain $status->core for backwards compatibility.
|
||||
$status->core = $core;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've made it this far, the core version has been checked.
|
||||
$core->checked = true;
|
||||
|
||||
// Generate a threat from core vulnerabilities.
|
||||
if ( is_array( $report_data->core->vulnerabilities ) && ! empty( $report_data->core->vulnerabilities ) ) {
|
||||
// normalize the vulnerabilities data
|
||||
$vulnerabilities = array_map(
|
||||
function ( $vulnerability ) {
|
||||
return new Vulnerability_Model( $vulnerability );
|
||||
},
|
||||
$report_data->core->vulnerabilities
|
||||
);
|
||||
|
||||
// convert the detected vulnerabilities into a vulnerable extension threat
|
||||
$threat = Threat_Model::generate_from_extension_vulnerabilities( $core, $vulnerabilities );
|
||||
|
||||
$threat_extension = clone $core;
|
||||
$extension_threat = clone $threat;
|
||||
|
||||
$core->threats[] = $extension_threat;
|
||||
$threat->extension = $threat_extension;
|
||||
|
||||
$status->threats[] = $threat;
|
||||
}
|
||||
|
||||
$status->core = $core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort By Threats
|
||||
*
|
||||
* @param array<Extension_Model> $threats Array of threats to sort.
|
||||
*
|
||||
* @return array<Extension_Model> The sorted $threats array.
|
||||
*/
|
||||
protected static function sort_threats( $threats ) {
|
||||
usort(
|
||||
$threats,
|
||||
function ( $a, $b ) {
|
||||
return count( $a->threats ) - count( $b->threats );
|
||||
}
|
||||
);
|
||||
|
||||
return $threats;
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* Class file for managing REST API endpoints for package protect-status.
|
||||
*
|
||||
* @package automattic/jetpack-protect-status
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Protect_Status;
|
||||
|
||||
use Automattic\Jetpack\Connection\Rest_Authentication;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class REST_Controller
|
||||
*/
|
||||
class REST_Controller {
|
||||
|
||||
/**
|
||||
* Initialize the plugin's REST API.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
// Set up the REST authentication hooks.
|
||||
Rest_Authentication::init();
|
||||
|
||||
// Add custom WP REST API endoints.
|
||||
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the REST API routes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register_rest_endpoints() {
|
||||
register_rest_route(
|
||||
'jetpack-protect/v1',
|
||||
'check-plan',
|
||||
array(
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => __CLASS__ . '::api_check_plan',
|
||||
'permission_callback' => function () {
|
||||
return current_user_can( 'manage_options' );
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
'jetpack-protect/v1',
|
||||
'status',
|
||||
array(
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => __CLASS__ . '::api_get_status',
|
||||
'permission_callback' => function () {
|
||||
return current_user_can( 'manage_options' );
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
'jetpack-protect/v1',
|
||||
'clear-scan-cache',
|
||||
array(
|
||||
'methods' => \WP_REST_Server::EDITABLE,
|
||||
'callback' => __CLASS__ . '::api_clear_scan_cache',
|
||||
'permission_callback' => function () {
|
||||
return current_user_can( 'manage_options' );
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return site plan data for the API endpoint
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public static function api_check_plan() {
|
||||
$has_required_plan = Plan::has_required_plan();
|
||||
|
||||
return rest_ensure_response( $has_required_plan );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Protect Status for the API endpoint
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public static function api_get_status( $request ) {
|
||||
$status = Status::get_status( $request['hard_refresh'] );
|
||||
return rest_ensure_response( $status );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the Scan_Status cache for the API endpoint
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public static function api_clear_scan_cache() {
|
||||
$cache_cleared = Scan_Status::delete_option();
|
||||
|
||||
if ( ! $cache_cleared ) {
|
||||
return new WP_REST_Response( 'An error occurred while attempting to clear the Jetpack Scan cache.', 500 );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( 'Jetpack Scan cache cleared.' );
|
||||
}
|
||||
}
|
||||
+365
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to handle the Scan Status of Jetpack Protect
|
||||
*
|
||||
* @phan-suppress PhanDeprecatedFunction -- Maintaining backwards compatibility.
|
||||
*
|
||||
* @package automattic/jetpack-protect-status
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Protect_Status;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Plugins_Installer;
|
||||
use Automattic\Jetpack\Protect_Models\Extension_Model;
|
||||
use Automattic\Jetpack\Protect_Models\Status_Model;
|
||||
use Automattic\Jetpack\Protect_Models\Threat_Model;
|
||||
use Automattic\Jetpack\Sync\Functions as Sync_Functions;
|
||||
use Jetpack_Options;
|
||||
use WP_Error;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that handles fetching of threats from the Scan API
|
||||
*/
|
||||
class Scan_Status extends Status {
|
||||
|
||||
/**
|
||||
* Scan endpoint
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SCAN_API_BASE = '/sites/%d/scan';
|
||||
|
||||
/**
|
||||
* Name of the option where status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_NAME = 'jetpack_scan_status';
|
||||
|
||||
/**
|
||||
* Name of the option where the timestamp of the status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TIMESTAMP_NAME = 'jetpack_scan_status_timestamp';
|
||||
|
||||
/**
|
||||
* Time in seconds that the cache should last
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const OPTION_EXPIRES_AFTER = 300; // 5 minutes.
|
||||
|
||||
/**
|
||||
* Gets the current status of the Jetpack Protect checks
|
||||
*
|
||||
* @param bool $refresh_from_wpcom Refresh the local plan and status cache from wpcom.
|
||||
* @return Status_Model
|
||||
*/
|
||||
public static function get_status( $refresh_from_wpcom = false ) {
|
||||
if ( self::$status !== null ) {
|
||||
return self::$status;
|
||||
}
|
||||
|
||||
if ( $refresh_from_wpcom || ! self::should_use_cache() || self::is_cache_expired() ) {
|
||||
$status = self::fetch_from_api();
|
||||
} else {
|
||||
$status = self::get_from_options();
|
||||
}
|
||||
|
||||
if ( is_wp_error( $status ) ) {
|
||||
$status = new Status_Model(
|
||||
array(
|
||||
'error' => true,
|
||||
'error_code' => $status->get_error_code(),
|
||||
'error_message' => $status->get_error_message(),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$status = self::normalize_api_data( $status );
|
||||
}
|
||||
|
||||
self::$status = $status;
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Scan API endpoint
|
||||
*
|
||||
* @return WP_Error|string
|
||||
*/
|
||||
public static function get_api_url() {
|
||||
$blog_id = Jetpack_Options::get_option( 'id' );
|
||||
$is_connected = ( new Connection_Manager() )->is_connected();
|
||||
|
||||
if ( ! $blog_id || ! $is_connected ) {
|
||||
return new WP_Error( 'site_not_connected' );
|
||||
}
|
||||
|
||||
$api_url = sprintf( self::SCAN_API_BASE, $blog_id );
|
||||
|
||||
return $api_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the status data from the Scan API
|
||||
*
|
||||
* @return WP_Error|array
|
||||
*/
|
||||
public static function fetch_from_api() {
|
||||
$api_url = self::get_api_url();
|
||||
if ( is_wp_error( $api_url ) ) {
|
||||
return $api_url;
|
||||
}
|
||||
|
||||
$response = Client::wpcom_json_api_request_as_blog(
|
||||
self::get_api_url(),
|
||||
'2',
|
||||
array(
|
||||
'method' => 'GET',
|
||||
'timeout' => 30,
|
||||
),
|
||||
null,
|
||||
'wpcom'
|
||||
);
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( is_wp_error( $response ) || 200 !== $response_code || empty( $response['body'] ) ) {
|
||||
return new WP_Error( 'failed_fetching_status', 'Failed to fetch Scan data from the server', array( 'status' => $response_code ) );
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
self::update_status_option( $body );
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize API Data
|
||||
*
|
||||
* Formats the payload from the Scan API into an instance of Status_Model.
|
||||
*
|
||||
* @phan-suppress PhanDeprecatedProperty -- Maintaining backwards compatibility.
|
||||
*
|
||||
* @param object $scan_data The data returned by the scan API.
|
||||
*
|
||||
* @return Status_Model
|
||||
*/
|
||||
private static function normalize_api_data( $scan_data ) {
|
||||
global $wp_version;
|
||||
|
||||
$installed_plugins = Plugins_Installer::get_plugins();
|
||||
$installed_themes = Sync_Functions::get_themes();
|
||||
|
||||
$plugins = array();
|
||||
$themes = array();
|
||||
$core = new Extension_Model(
|
||||
array(
|
||||
'name' => 'WordPress',
|
||||
'slug' => 'wordpress',
|
||||
'version' => $wp_version,
|
||||
'type' => 'core',
|
||||
'checked' => true, // to do: default to false once Scan API has manifest
|
||||
)
|
||||
);
|
||||
$files = array();
|
||||
$database = array();
|
||||
|
||||
$status = new Status_Model(
|
||||
array(
|
||||
'data_source' => 'scan_api',
|
||||
'status' => isset( $scan_data->state ) ? $scan_data->state : null,
|
||||
'num_threats' => 0,
|
||||
'num_themes_threats' => 0,
|
||||
'num_plugins_threats' => 0,
|
||||
'has_unchecked_items' => false,
|
||||
'current_progress' => isset( $scan_data->current->progress ) ? $scan_data->current->progress : null,
|
||||
)
|
||||
);
|
||||
|
||||
// Format the "last checked" timestamp.
|
||||
if ( ! empty( $scan_data->most_recent->timestamp ) ) {
|
||||
$date = new \DateTime( $scan_data->most_recent->timestamp );
|
||||
$status->last_checked = $date->format( 'Y-m-d H:i:s' );
|
||||
}
|
||||
|
||||
// Ensure all installed plugins and themes are represented in the status.
|
||||
foreach ( $installed_plugins as $path => $installed_plugin ) {
|
||||
$slug = str_replace( '.php', '', explode( '/', $path )[0] );
|
||||
$plugin = new Extension_Model(
|
||||
array(
|
||||
'name' => $installed_plugin['Name'],
|
||||
'version' => $installed_plugin['Version'],
|
||||
'slug' => $slug,
|
||||
'type' => 'plugins',
|
||||
'checked' => true, // to do: default to false once Scan API has manifest
|
||||
)
|
||||
);
|
||||
|
||||
$plugins[ $slug ] = $plugin;
|
||||
}
|
||||
foreach ( $installed_themes as $path => $installed_theme ) {
|
||||
$slug = str_replace( '.php', '', explode( '/', $path )[0] );
|
||||
$theme = new Extension_Model(
|
||||
array(
|
||||
'name' => $installed_theme['Name'],
|
||||
'version' => $installed_theme['Version'],
|
||||
'slug' => $slug,
|
||||
'type' => 'themes',
|
||||
'checked' => true, // to do: default to false once Scan API has manifest
|
||||
)
|
||||
);
|
||||
|
||||
$themes[ $slug ] = $theme;
|
||||
}
|
||||
|
||||
// Merge the threats into the status model.
|
||||
if ( isset( $scan_data->threats ) && is_array( $scan_data->threats ) ) {
|
||||
foreach ( $scan_data->threats as $scan_threat ) {
|
||||
if ( isset( $scan_threat->fixable ) && $scan_threat->fixable ) {
|
||||
$status->fixable_threat_ids[] = $scan_threat->id;
|
||||
}
|
||||
|
||||
$db_details = null;
|
||||
if ( ! empty( $scan_threat->table ) ) {
|
||||
$db_details = (object) array_merge(
|
||||
array(
|
||||
'table' => $scan_threat->table,
|
||||
'pk_column' => $scan_threat->pk_column ?? null,
|
||||
'pk_value' => $scan_threat->value ?? null,
|
||||
),
|
||||
! empty( $scan_threat->details ) ? array( 'details' => $scan_threat->details ) : array()
|
||||
);
|
||||
}
|
||||
|
||||
$threat = new Threat_Model(
|
||||
array(
|
||||
'id' => $scan_threat->id ?? null,
|
||||
'signature' => $scan_threat->signature ?? null,
|
||||
'title' => $scan_threat->title ?? null,
|
||||
'description' => $scan_threat->description ?? null,
|
||||
'vulnerability_description' => $scan_threat->vulnerability_description ?? null,
|
||||
'extension' => $scan_threat->extension ?? null,
|
||||
'fix_description' => $scan_threat->fix_description ?? null,
|
||||
'payload_subtitle' => $scan_threat->payload_subtitle ?? null,
|
||||
'payload_description' => $scan_threat->payload_description ?? null,
|
||||
'first_detected' => $scan_threat->first_detected ?? null,
|
||||
'fixed_in' => isset( $scan_threat->fixer->fixer ) && 'update' === $scan_threat->fixer->fixer ? $scan_threat->fixer->target : null,
|
||||
'severity' => $scan_threat->severity ?? null,
|
||||
'fixable' => $scan_threat->fixer ?? null,
|
||||
'status' => $scan_threat->status ?? null,
|
||||
'filename' => $scan_threat->filename ?? null,
|
||||
'context' => $scan_threat->context ?? null,
|
||||
'source' => $scan_threat->source ?? null,
|
||||
'table' => $scan_threat->table ?? null,
|
||||
'details' => $db_details,
|
||||
)
|
||||
);
|
||||
|
||||
// Theme and Plugin Threats
|
||||
if ( ! empty( $scan_threat->extension ) && in_array( $scan_threat->extension->type, array( 'plugin', 'theme' ), true ) ) {
|
||||
$installed_extension = 'plugin' === $scan_threat->extension->type ? ( $plugins[ $scan_threat->extension->slug ] ?? null ) : ( $themes[ $scan_threat->extension->slug ] ?? null );
|
||||
|
||||
// If the extension is no longer installed, skip this threat.
|
||||
// todo: use version_compare()
|
||||
if ( ! $installed_extension ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Push the threat to the appropriate extension.
|
||||
switch ( $scan_threat->extension->type ) {
|
||||
case 'plugin':
|
||||
$plugins[ $scan_threat->extension->slug ]->threats[] = clone $threat;
|
||||
++$status->num_plugins_threats;
|
||||
break;
|
||||
case 'theme':
|
||||
$themes[ $scan_threat->extension->slug ]->threats[] = clone $threat;
|
||||
++$status->num_themes_threats;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$threat->extension = new Extension_Model(
|
||||
array(
|
||||
'name' => isset( $scan_threat->extension->name ) ? $scan_threat->extension->name : null,
|
||||
'slug' => isset( $scan_threat->extension->slug ) ? $scan_threat->extension->slug : null,
|
||||
'version' => isset( $scan_threat->extension->version ) ? $scan_threat->extension->version : null,
|
||||
'type' => $scan_threat->extension->type . 's',
|
||||
'checked' => $installed_extension->version === $scan_threat->extension->version,
|
||||
)
|
||||
);
|
||||
} elseif ( isset( $threat->signature ) && 'Vulnerable.WP.Core' === $threat->signature ) {
|
||||
// Vulnerable WordPress Core Version Threats
|
||||
|
||||
// If the core version has changed, skip this threat.
|
||||
// todo: use version_compare()
|
||||
if ( $scan_threat->version !== $wp_version ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$core->threats[] = $threat;
|
||||
} elseif ( ! empty( $threat->filename ) ) {
|
||||
// File Threats
|
||||
$files[] = $threat;
|
||||
} elseif ( ! empty( $scan_threat->table ) ) {
|
||||
// Database Threats
|
||||
$database[] = $threat;
|
||||
}
|
||||
|
||||
$status->threats[] = $threat;
|
||||
++$status->num_threats;
|
||||
}
|
||||
}
|
||||
|
||||
$status->threats = static::sort_threats( $status->threats );
|
||||
|
||||
// maintain deprecated properties for backwards compatibility
|
||||
$status->plugins = array_values( $plugins );
|
||||
$status->themes = array_values( $themes );
|
||||
$status->core = $core;
|
||||
$status->files = $files;
|
||||
$status->database = $database;
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort By Threats
|
||||
*
|
||||
* @param array<Threat_Model> $threats Array of threats to sort.
|
||||
*
|
||||
* @return array<Threat_Model> The sorted $threats array.
|
||||
*/
|
||||
protected static function sort_threats( $threats ) {
|
||||
usort(
|
||||
$threats,
|
||||
function ( $a, $b ) {
|
||||
// Order by active status first...
|
||||
if ( $a->status !== $b->status ) {
|
||||
return 'active' === $a->status ? -1 : 1;
|
||||
}
|
||||
|
||||
// ...then by severity...
|
||||
if ( $a->severity !== $b->severity ) {
|
||||
return $a->severity > $b->severity ? -1 : 1;
|
||||
}
|
||||
|
||||
// ...then date added.
|
||||
if ( $a->first_detected !== $b->first_detected ) {
|
||||
return strtotime( $a->first_detected ) < strtotime( $b->first_detected ) ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
|
||||
return $threats;
|
||||
}
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to handle the Status of Jetpack Protect
|
||||
*
|
||||
* @package automattic/jetpack-protect-status
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Protect_Status;
|
||||
|
||||
use Automattic\Jetpack\Protect_Models\Status_Model;
|
||||
|
||||
/**
|
||||
* Class that handles fetching and caching the Status of vulnerabilities check from the WPCOM servers
|
||||
*/
|
||||
class Status {
|
||||
|
||||
const PACKAGE_VERSION = '0.7.8';
|
||||
/**
|
||||
* Name of the option where status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_NAME = '';
|
||||
|
||||
/**
|
||||
* Name of the option where the timestamp of the status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TIMESTAMP_NAME = '';
|
||||
|
||||
/**
|
||||
* Time in seconds that the cache should last
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const OPTION_EXPIRES_AFTER = 3600; // 1 hour.
|
||||
|
||||
/**
|
||||
* Time in seconds that the cache for the initial empty response should last
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const INITIAL_OPTION_EXPIRES_AFTER = 1 * MINUTE_IN_SECONDS;
|
||||
|
||||
/**
|
||||
* Memoization for the current status
|
||||
*
|
||||
* @var null|Status_Model
|
||||
*/
|
||||
public static $status = null;
|
||||
|
||||
/**
|
||||
* Gets the current status of the Jetpack Protect checks
|
||||
*
|
||||
* @param bool $refresh_from_wpcom Refresh the local plan and status cache from wpcom.
|
||||
* @return Status_Model
|
||||
*/
|
||||
public static function get_status( $refresh_from_wpcom = false ) {
|
||||
$use_scan_status = Plan::has_required_plan( $refresh_from_wpcom );
|
||||
|
||||
if ( defined( 'JETPACK_PROTECT_DEV__DATA_SOURCE' ) ) {
|
||||
if ( 'scan_api' === JETPACK_PROTECT_DEV__DATA_SOURCE ) {
|
||||
$use_scan_status = true;
|
||||
}
|
||||
|
||||
if ( 'protect_report' === JETPACK_PROTECT_DEV__DATA_SOURCE ) {
|
||||
$use_scan_status = false;
|
||||
}
|
||||
}
|
||||
|
||||
self::$status = $use_scan_status ? Scan_Status::get_status( $refresh_from_wpcom ) : Protect_Status::get_status( $refresh_from_wpcom );
|
||||
return self::$status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current cached status is expired and should be renewed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_cache_expired() {
|
||||
$option_timestamp = get_option( static::OPTION_TIMESTAMP_NAME );
|
||||
|
||||
if ( ! $option_timestamp ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return time() > (int) $option_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we should consider the stored cache or bypass it
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function should_use_cache() {
|
||||
return defined( 'JETPACK_PROTECT_DEV__BYPASS_CACHE' ) && JETPACK_PROTECT_DEV__BYPASS_CACHE ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current cached status
|
||||
*
|
||||
* @return bool|array False if value is not found. Array with values if cache is found.
|
||||
*/
|
||||
public static function get_from_options() {
|
||||
return maybe_unserialize( get_option( static::OPTION_NAME ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated the cached status and its timestamp
|
||||
*
|
||||
* @param array $status The new status to be cached.
|
||||
* @return void
|
||||
*/
|
||||
public static function update_status_option( $status ) {
|
||||
// TODO: Sanitize $status.
|
||||
update_option( static::OPTION_NAME, maybe_serialize( $status ) );
|
||||
$end_date = self::get_cache_end_date_by_status( $status );
|
||||
update_option( static::OPTION_TIMESTAMP_NAME, $end_date );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp the cache should expire depending on the current status
|
||||
*
|
||||
* Initial empty status, which are returned before the first check was performed, should be cache for less time
|
||||
*
|
||||
* @param object $status The response from the server being cached.
|
||||
* @return int The timestamp when the cache should expire.
|
||||
*/
|
||||
public static function get_cache_end_date_by_status( $status ) {
|
||||
if ( ! is_object( $status ) || empty( $status->last_checked ) ) {
|
||||
return time() + static::INITIAL_OPTION_EXPIRES_AFTER;
|
||||
}
|
||||
return time() + static::OPTION_EXPIRES_AFTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the cached status and its timestamp
|
||||
*
|
||||
* @return bool Whether all related status options were successfully deleted.
|
||||
*/
|
||||
public static function delete_option() {
|
||||
$option_deleted = delete_option( static::OPTION_NAME );
|
||||
$option_timestamp_deleted = delete_option( static::OPTION_TIMESTAMP_NAME );
|
||||
|
||||
return $option_deleted && $option_timestamp_deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current status to see if there are any threats found
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_threats() {
|
||||
return 0 < self::get_total_threats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of threats found
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function get_total_threats() {
|
||||
$status = static::get_status();
|
||||
return count( $status->threats );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all threats combined
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_all_threats() {
|
||||
$status = static::get_status();
|
||||
return $status->threats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get threats found for WordPress core
|
||||
*
|
||||
* @deprecated 0.3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_wordpress_threats() {
|
||||
return self::get_threats( 'core' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get threats found for themes
|
||||
*
|
||||
* @deprecated 0.3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_themes_threats() {
|
||||
return self::get_threats( 'themes' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get threats found for plugins
|
||||
*
|
||||
* @deprecated 0.3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_plugins_threats() {
|
||||
return self::get_threats( 'plugins' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get threats found for files
|
||||
*
|
||||
* @deprecated 0.3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_files_threats() {
|
||||
return self::get_threats( 'files' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get threats found for plugins
|
||||
*
|
||||
* @deprecated 0.3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_database_threats() {
|
||||
return self::get_threats( 'database' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the threats for one type of extension or core
|
||||
*
|
||||
* @param string $type What threats you want to get. Possible values are 'core', 'themes' and 'plugins'.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_threats( $type ) {
|
||||
$status = static::get_status();
|
||||
|
||||
if ( in_array( $type, array( 'plugin', 'theme', 'core' ), true ) ) {
|
||||
return array_filter(
|
||||
$status->threats,
|
||||
function ( $threat ) use ( $type ) {
|
||||
return isset( $threat->extension ) && $type === $threat->extension->type;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'files' === $type ) {
|
||||
return array_filter(
|
||||
$status->threats,
|
||||
function ( $threat ) {
|
||||
return ! empty( $threat->filename );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'database' === $type ) {
|
||||
return array_filter(
|
||||
$status->threats,
|
||||
function ( $threat ) {
|
||||
return ! empty( $threat->table );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $status->threats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort By Threats
|
||||
*
|
||||
* @deprecated 0.3.0
|
||||
*
|
||||
* @param array<object> $threats Array of threats to sort.
|
||||
*
|
||||
* @return array<object> The sorted $threats array.
|
||||
*/
|
||||
protected static function sort_threats( $threats ) {
|
||||
usort(
|
||||
$threats,
|
||||
function ( $a, $b ) {
|
||||
// sort primarily based on the presence of threats
|
||||
$ret = empty( $a->threats ) <=> empty( $b->threats );
|
||||
|
||||
// sort secondarily on whether the item has been checked
|
||||
if ( ! $ret ) {
|
||||
$ret = $a->checked <=> $b->checked;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
);
|
||||
|
||||
return $threats;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user