Compare commits

..

5 Commits

Author SHA1 Message Date
Fred Tingaud
6c26d30bed Add a trigger for coverage runs 2024-07-16 11:10:57 +02:00
Fred Tingaud
9fef286c33 Revert git clone 2024-07-16 11:10:14 +02:00
Fred Tingaud
66dfb14591 Revert "BUILD-4733: Use COVERAGE_GITHUB_TOKEN from the vault instead of the default GITHUB_TOKEN"
This reverts commit 9a14e956754adfffdfb53bd22d4a230cb97acaae.
2024-07-16 10:21:35 +02:00
Fred Tingaud
3e7a5a4108 Revert "BUILD-5580: Add missing id-token: write permissions for the Vault action and update the action to v3"
This reverts commit 1c285ade7a6638afc8d00893d9756b567fd9c60c.
2024-07-16 10:20:53 +02:00
Fred Tingaud
e2375b3b06 Revert Coverage script to functional state 2024-07-16 10:18:15 +02:00
3294 changed files with 11097 additions and 66995 deletions

View File

@ -1,4 +1,4 @@
load("github.com/SonarSource/cirrus-modules@v3", "load_features")
load("github.com/SonarSource/cirrus-modules@v2", "load_features")
def main(ctx):
return load_features(ctx)

View File

@ -25,7 +25,7 @@ container_definition: &CONTAINER_DEFINITION
setup_sonar_scanner: &SETUP_SONAR_SCANNER
setup_sonar_scanner_script:
- apt update -y && apt upgrade -y && apt update -y && apt install -y unzip
- apt update -y && apt upgrade -y && apt update -y && apt install -y unzip
- curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
- unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
@ -38,7 +38,9 @@ tooling_tests_task:
env:
PYTHONPATH: .
install_dependencies_script:
- ci/install_rspec_tools_dependencies.sh
- cd rspec-tools
- pipenv install --dev
- pipenv run pip install pytest pytest-cov
tests_script:
- bash ci/fetch_branches.sh
- cd rspec-tools
@ -75,6 +77,15 @@ frontend_tests_task:
- cd frontend
- sonar-scanner
validate_metadata_task:
eks_container:
<<: *CONTAINER_DEFINITION
dockerfile: ci/Dockerfile
cpu: 1
memory: 2G
metadata_tests_script:
- ./ci/validate_metadata.sh
validate_ci_tests_task:
skip: "!changesInclude('ci_tests/**', 'ci/**')"
eks_container:
@ -85,22 +96,17 @@ validate_ci_tests_task:
ci_tests_script:
- ./ci_tests/asciidoc_validation/run_tests.sh
validate_rules_task:
validate_asciidoc_task:
eks_container:
<<: *CONTAINER_DEFINITION
dockerfile: ci/Dockerfile
cpu: 1
memory: 2G
metadata_validation_script:
- ./ci/validate_metadata.sh
file_extensions_validation_script:
- ./ci/validate_file_extensions.sh
asciidoc_validation_script:
asciidoc_tests_script:
- ./ci/validate_asciidoc.sh
validate_links_task:
timeout_in: 120m
execution_lock: RSPEC_validate_links
eks_container:
<<: *CONTAINER_DEFINITION
dockerfile: ci/Dockerfile
@ -111,20 +117,31 @@ validate_links_task:
LINK_CACHE_PATH: /root/link-probing-history.cache
cache_download_script:
- bash ci/cirrus-cache.sh download ${LINK_CACHE_NAME} ${LINK_CACHE_PATH}
- md5sum /root/link-probing-history.cache/link_probes.history || true
tests_script:
- md5sum /root/link-probing-history.cache/link_probes.history || true
- ./ci/validate_links.sh ${LINK_CACHE_PATH}
- md5sum /root/link-probing-history.cache/link_probes.history
always:
cache_upload_script:
- md5sum /root/link-probing-history.cache/link_probes.history || true
- bash ci/cirrus-cache.sh upload ${LINK_CACHE_NAME} ${LINK_CACHE_PATH}
validate_file_extensions_task:
eks_container:
<<: *CONTAINER_DEFINITION
dockerfile: ci/Dockerfile
cpu: 1
memory: 2G
file_extension_tests_script:
- bash ./ci/validate_file_extensions.sh
all_required_checks_task:
depends_on:
- tooling_tests
- frontend_tests
- validate_rules
- validate_metadata
- validate_asciidoc
- validate_ci_tests
- validate_file_extensions
eks_container:
<<: *CONTAINER_DEFINITION
dockerfile: ci/Dockerfile

View File

@ -1,16 +0,0 @@
{
"build": {
"dockerfile": "../ci/Dockerfile"
},
// https://code.visualstudio.com/docs/devcontainers/create-dev-container#_rebuild
"postCreateCommand": ".devcontainer/finalize-container.sh",
"waitFor": "postCreateCommand",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"asciidoctor.asciidoctor-vscode"
]
}
}
}

View File

@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
TOP_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)/..
$TOP_DIR/ci/install_rspec_tools_dependencies.sh

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
.github/CODEOWNERS @sonarsource/quality-cfamily-squad
.github/CODEOWNERS @sonarsource/analysis-cfamily-squad

View File

@ -1,12 +1,3 @@
<!--
Jira Automation:
* Mention existing issue in the PR title to move it around automatically.
* Mention existing issue in the PR description and a sub-task will be created for you to track this rspec PR separately.
No issue is created by default.
-->
## Review
A dedicated reviewer checked the rule description successfully for:

View File

@ -1,28 +0,0 @@
name: Pull Request Closed
on:
pull_request:
types: [closed]
jobs:
PullRequestMerged_job:
name: Pull Request Merged
runs-on: ubuntu-latest
permissions:
id-token: write
pull-requests: read
# For external PR, ticket should be moved manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/PullRequestClosed@v2
with:
github-token: ${{secrets.GITHUB_TOKEN}}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}

View File

@ -1,28 +0,0 @@
name: Pull Request Created
on:
pull_request:
types: ["opened"]
jobs:
PullRequestCreated_job:
name: Pull Request Created
runs-on: ubuntu-latest
permissions:
id-token: write
# For external PR, ticket should be created manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN;
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/PullRequestCreated@v2
with:
github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}

View File

@ -1,28 +0,0 @@
name: Request review
on:
pull_request:
types: ["review_requested"]
jobs:
RequestReview_job:
name: Request review
runs-on: ubuntu-latest
permissions:
id-token: write
# For external PR, ticket should be moved manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN;
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/RequestReview@v2
with:
github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}

View File

@ -1,30 +0,0 @@
name: Submit Review
on:
pull_request_review:
types: [submitted]
jobs:
SubmitReview_job:
name: Submit Review
runs-on: ubuntu-latest
permissions:
id-token: write
pull-requests: read
# For external PR, ticket should be moved manually
if: |
github.event.pull_request.head.repo.full_name == github.repository
&& (github.event.review.state == 'changes_requested'
|| github.event.review.state == 'approved')
steps:
- id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/kv/data/jira user | JIRA_USER;
development/kv/data/jira token | JIRA_TOKEN;
- uses: sonarsource/gh-action-lt-backlog/SubmitReview@v2
with:
github-token: ${{secrets.GITHUB_TOKEN}}
jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}

View File

@ -15,9 +15,6 @@ on:
jobs:
add_language_to_rule:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -12,9 +12,6 @@ on:
jobs:
create_new_rule:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -7,10 +7,11 @@ on:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
permissions:
pull-requests: read # Get the list and metadata of open new-rule PRs
contents: write # Get the contents of open new-rule PRs, the 'master'; write to 'gh-pages' branch
pages: write # for github-pages-deploy-action
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4 # If you're using actions/checkout you must set persist-credentials to false in most cases for the deployment to work correctly.

View File

@ -2,34 +2,20 @@ name: Update rule coverage
on:
schedule:
- cron: '17 2 * * *'
workflow_dispatch: # When manually triggered from a non-default branch, the results will not be pushed
workflow_dispatch
jobs:
update_coverage:
runs-on: ubuntu-latest
permissions:
id-token: write # required by SonarSource/vault-action-wrapper
contents: write
actions: write # required by andymckay/cancel-action
env:
TMP_BRANCH: temporary/coverage_update
steps:
- name: 'get secrets'
id: secrets
uses: SonarSource/vault-action-wrapper@v3
with:
secrets: |
development/github/token/SonarSource-rspec-coverage token | coverage_github_token;
development/kv/data/slack token | slack_token;
- uses: actions/checkout@v4
with:
persist-credentials: true
fetch-depth: 0
ref: master
path: 'rspec'
token: ${{ fromJSON(steps.secrets.outputs.vault).coverage_github_token }}
ref: 'master'
token: ${{ secrets.COVERAGE_GITHUB_TOKEN }}
- uses: actions/setup-python@v4
with:
@ -45,7 +31,7 @@ jobs:
- name: 'Regenerate coverage information'
env:
GITHUB_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).coverage_github_token }}
GITHUB_TOKEN: ${{ secrets.COVERAGE_GITHUB_TOKEN }}
id: gen-coverage
working-directory: 'rspec/rspec-tools'
run: |
@ -73,67 +59,44 @@ jobs:
git commit -m "update coverage information"
git push --force-with-lease origin $TMP_BRANCH
- name: 'Create a PR'
id: create-github-pr
working-directory: 'rspec'
env:
GH_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).coverage_github_token }}
run: |
PR_URL=$(gh pr create --head ${{ env.TMP_BRANCH }} --title "Update coverage information" --body "" --label "rspec system")
gh pr merge $PR_URL
- name: 'Wait for CI to succeed'
if: steps.gen-coverage.outputs.new_coverage == 'true'
uses: fountainhead/action-wait-for-check@v1.0.0
id: wait-for-build
with:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: all_required_checks
ref: ${{ env.TMP_BRANCH }}
timeoutSeconds: 2400
intervalSeconds: 30
- name: 'Wait until the PR is merged'
id: wait-for-pr-to-merge
env:
GH_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).coverage_github_token }}
- name: 'Push the updated coverage to master'
if: |
steps.gen-coverage.outputs.new_coverage == 'true' &&
steps.wait-for-build.outputs.conclusion == 'success'
working-directory: 'rspec'
run: |
set -ueo pipefail
git checkout master
git merge $TMP_BRANCH
git push origin master
# Implicitly referring to the PR corresponding to current branch
- name: 'Delete the temporary branch'
if: always() && steps.create-temp-branch.conclusion == 'success'
uses: dawidd6/action-delete-branch@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN}}
branches: ${{ env.TMP_BRANCH}}
# Set timeout (20 minutes in seconds)
TIMEOUT=1200 # seconds
START_TIME=$(date +%s)
INTERVAL=20 # seconds
while true; do
# Check if the PR is merged
PR_STATE=$(gh pr view --json state,mergedAt -q '.state')
MERGED_AT=$(gh pr view --json state,mergedAt -q '.mergedAt')
if [[ "${PR_STATE}" == "MERGED" ]]; then
echo "PR merged at: $MERGED_AT"
exit 0
fi
echo "PR state is ${PR_STATE}"
# Check for timeout
CURRENT_TIME=$(date +%s)
ELAPSED_TIME=$((CURRENT_TIME - START_TIME))
if [[ "${ELAPSED_TIME}" -gt "${TIMEOUT}" ]]; then
echo "Timeout waiting for PR to merge."
exit 1
fi
# Wait for $INTERVAL seconds before checking again
sleep "$INTERVAL"
done
- name: 'Close PR and delete branch upon failure to merge'
if: ${{ failure() }}
env:
GH_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).coverage_github_token }}
working-directory: 'rspec'
run: |
PR_URL=$(gh pr view --json url --jq '.url')
gh pr close "$PR_URL" --delete-branch
- name: 'Fail if the change breaks CI'
if: |
steps.gen-coverage.outputs.new_coverage == 'true' &&
steps.wait-for-build.outputs.conclusion != 'success'
run: exit 1
- name: 'Notify on slack about the failure'
if: ${{ failure() }}
env:
SLACK_API_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).slack_token }}
SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }}
working-directory: 'rspec/rspec-tools'
run: |
pipenv run rspec-tools notify-failure-on-slack \

View File

@ -25,10 +25,7 @@ on:
jobs:
update_quickfix_status:
name: Update quick fix status
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-20.04
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:

1
.gitignore vendored
View File

@ -10,7 +10,6 @@
# generated files
/rules/**/*.html
/frontend/public/rules
rspec-tools/link_probes.history
# compiled files
*.out

275
LICENSE
View File

@ -1,184 +1,165 @@
SONAR Source-Available License v1.0
Last Updated November 13, 2024
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
1. DEFINITIONS
Copyright (C) 2007 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.
"Agreement" means this Sonar Source-Available License v1.0
"Competing" means marketing a product or service as a substitute for the
functionality or value of SonarQube. A product or service may compete regardless
of how it is designed or deployed. For example, a product or service may compete
even if it provides its functionality via any kind of interface (including
services, libraries, or plug-ins), even if it is ported to a different platform
or programming language, and even if it is provided free of charge.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
"Contribution" means:
0. Additional Definitions.
a) in the case of the initial Contributor, the initial content Distributed under
this Agreement, and
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
where such changes and/or additions to the Program originate from and are
Distributed by that particular Contributor. A Contribution "originates" from a
Contributor if it was added to the Program by such Contributor itself or anyone
acting on such Contributor's behalf. Contributions do not include changes or
additions to the Program that are not Modified Works.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
"Contributor" means any person or entity that Distributes the Program.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
"Derivative Works" shall mean any work, whether in Source Code or other form,
that is based on (or derived from) the Program and for which the editorial
revisions, annotations, elaborations, or other modifications represent, as a
whole, an original work of authorship.
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
"Distribute" means the acts of a) distributing or b) making available in any
manner that enables the transfer of a copy.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
"Licensed Patents" mean patent claims licensable by a Contributor that are
necessarily infringed by the use or sale of its Contribution alone or when
combined with the Program.
1. Exception to Section 3 of the GNU GPL.
"Modified Works" shall mean any work in Source Code or other form that results
from an addition to, deletion from, or modification of the contents of the
Program, including, for purposes of clarity, any new file in Source Code form
that contains any contents of the Program. Modified Works shall not include
works that contain only declarations, interfaces, types, classes, structures, or
files of the Program solely in each case in order to link to, bind by name, or
subclass the Program or Modified Works thereof.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
"Non-competitive Purpose" means any purpose except for (a) providing to others
any product or service that includes or offers the same or substantially similar
functionality as SonarQube, (b) Competing with SonarQube, and/or (c) employing,
using, or engaging artificial intelligence technology that is not part of the
Program to ingest, interpret, analyze, train on, or interact with the data
provided by the Program, or to engage with the Program in any manner.
2. Conveying Modified Versions.
"Notices" means any legal statements or attributions included with the Program,
including, without limitation, statements concerning copyright, patent,
trademark, disclaimers of warranty, or limitations of liability
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
"Program" means the Contributions Distributed in accordance with this Agreement.
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
"Recipient" means anyone who receives the Program under this Agreement,
including Contributors.
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
"SonarQube" means an open-source or commercial edition of software offered by
SonarSource that is branded "SonarQube".
3. Object Code Incorporating Material from Library Header Files.
"SonarSource" means SonarSource SA, a Swiss company registered in Switzerland
under UID No. CHE-114.587.664.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
"Source Code" means the form of a Program preferred for making modifications,
including but not limited to software source code, documentation source, and
configuration files.
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
2. GRANT OF RIGHTS
b) Accompany the object code with a copy of the GNU GPL and this license
document.
a) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free copyright license, for any
Non-competitive Purpose, to reproduce, prepare Derivative Works of, publicly
display, publicly perform, Distribute and sublicense the Contribution of such
Contributor, if any, and such Derivative Works.
4. Combined Works.
b) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed
Patents, for any Non-competitive Purpose, to make, use, sell, offer to sell,
import, and otherwise transfer the Contribution of such Contributor, if any, in
Source Code or other form. This patent license shall apply to the combination of
the Contribution and the Program if, at the time the Contribution is added by
the Contributor, such addition of the Contribution causes such combination to be
covered by the Licensed Patents. The patent license shall not apply to any other
combinations that include the Contribution.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
c) Recipient understands that although each Contributor grants the licenses to
its Contributions set forth herein, no assurances are provided by any
Contributor that the Program does not infringe the patent or other intellectual
property rights of any other entity. Each Contributor disclaims any liability to
Recipient for claims brought by any other entity based on infringement of
intellectual property rights or otherwise. As a condition to exercising the
rights and licenses granted hereunder, each Recipient hereby assumes sole
responsibility to secure any other intellectual property rights needed, if any.
For example, if a third-party patent license is required to allow Recipient to
Distribute the Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
d) Each Contributor represents that to its knowledge it has sufficient copyright
rights in its Contribution, if any, to grant the copyright license set forth in
this Agreement.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
3. REQUIREMENTS
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
3.1 If a Contributor Distributes the Program in any form, then the Program must
also be made available as Source Code, in accordance with section 3.2, and the
Contributor must accompany the Program with a statement that the Source Code for
the Program is available under this Agreement, and inform Recipients how to
obtain it in a reasonable manner on or through a medium customarily used for
software exchange; and
d) Do one of the following:
3.2 When the Program is Distributed as Source Code:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
a) it must be made available under this Agreement, and
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
b) a copy of this Agreement must be included with each copy of the Program.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
3.3 Contributors may not remove or alter any Notices contained within the
Program from any copy of the Program which they Distribute, provided that
Contributors may add their own appropriate Notices.
5. Combined Libraries.
4. NO WARRANTY
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY
APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and distributing the
Program and assumes all risks associated with its exercise of rights under this
Agreement, including but not limited to the risks and costs of program errors,
compliance with applicable laws, damage to or loss of data, programs or
equipment, and unavailability or interruption of operations.
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
5. DISCLAIMER OF LIABILITY
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY
APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF
THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGES.
6. Revised Versions of the GNU Lesser General Public License.
6. GENERAL
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser 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.
If any provision of this Agreement is invalid or unenforceable under applicable
law, it shall not affect the validity or enforceability of the remainder of the
terms of this Agreement, and without further action by the parties hereto, such
provision shall be reformed to the minimum extent necessary to make such
provision valid and enforceable.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If Recipient institutes patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
(excluding combinations of the Program with other software or hardware)
infringes such Recipients patent(s), then such Recipients rights granted under
Section 2(b) shall terminate as of the date such litigation is filed.
All Recipients rights under this Agreement shall terminate if it fails to
comply with any of the material terms or conditions of this Agreement and does
not cure such failure in a reasonable period of time after becoming aware of
such noncompliance. If all Recipients rights under this Agreement terminate,
Recipient agrees to cease use and distribution of the Program as soon as
reasonably practicable. However, Recipients obligations under this Agreement
and any licenses granted by Recipient relating to the Program shall continue and
survive.
Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives
no rights or licenses to the intellectual property of any Contributor under this
Agreement, whether expressly, by implication, estoppel, or otherwise. All rights
in the Program not expressly granted under this Agreement are reserved. Nothing
in this Agreement is intended to be enforceable by any entity that is not a
Contributor or Recipient. No third-party beneficiary rights are created under
this Agreement.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@ -8,8 +8,6 @@ This repository contains the specification of every static-analysis rule availab
It also contains rules which have been dropped and rules which will one day be implemented.
The content of this repository is covered by the link:LICENSE[SONAR Source-Available License v1.0].
== Rules directory structure
* https://github.com/SonarSource/rspec/tree/master/rules[rules] directory: contains every specified rule.

View File

@ -6,38 +6,38 @@ ACTION=${1}
CACHE_NAME=${2}
PATH_TO_CACHE=${3}
CACHE_URL="http://${CIRRUS_HTTP_CACHE_HOST}/${CACHE_NAME}"
CACHE_KEY=${CACHE_NAME}
DEFAULT_CACHE_KEY=${CACHE_NAME}
TMP_PATH="/tmp/tmp-cache.tgz"
CACHE_URL=http://${CIRRUS_HTTP_CACHE_HOST}/${CACHE_KEY}
TMP_PATH=/tmp/tmp-cache.tgz
case "${ACTION}" in
download)
echo "Download cache with key ${CACHE_NAME} from ${CACHE_URL}"
curl --silent --show-error --fail --location --output "${TMP_PATH}" "${CACHE_URL}" || {
echo "Cache download failed" >&2
exit 0
echo "Download cache with key ${CACHE_KEY}"
echo " -> try ${CACHE_URL}"
curl -sfSL -o ${TMP_PATH} ${CACHE_URL} || {
echo "Cache download failed";
exit 0;
}
du -hs "${TMP_PATH}"
tar -Pxzf "${TMP_PATH}"
rm "${TMP_PATH}"
du -hs ${TMP_PATH}
tar -Pxzf ${TMP_PATH}
rm ${TMP_PATH}
;;
upload)
echo "Upload cache to ${CACHE_URL}"
tar -Pczf "${TMP_PATH}" "${PATH_TO_CACHE}"
du -hs "${TMP_PATH}"
curl --silent --show-error -X POST --data-binary "@${TMP_PATH}" "${CACHE_URL}" || {
echo "Cache upload failed" >&2
exit 0
}
tar -Pczf ${TMP_PATH} ${PATH_TO_CACHE}
du -hs ${TMP_PATH}
curl -s -X POST --data-binary @${TMP_PATH} ${CACHE_URL}
;;
*)
echo "Unexpected cache ACTION: ${ACTION}" >&2
echo "Unexpected cache ACTION: ${ACTION}"
exit 1
;;
esac
echo "Cache ${ACTION}ed succeeded."

View File

@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cd rspec-tools
pipenv install --dev
pipenv run pip install pytest pytest-cov

0
ci/validate_file_extensions.sh Executable file → Normal file
View File

View File

@ -13,20 +13,17 @@ else
git diff --name-only "${base}" -- rules/ | # Get all the changes in rules
sed -Ee 's#(rules/S[0-9]+)/.*#\1#' | # extract the rule directories
sort -u | # deduplicate
while IFS= read -r rule; do if [[ -d "$rule" ]]; then echo "$rule"; fi done | # filter out deleted rules
while IFS= read -r rule; do [[ -d "$rule" ]] && echo "$rule" || true; done | # filter non-deleted rules
sed 's#rules/##' | # get rule ids
mapfile -t affected_rules # store them in the `affected_rules` array
echo "Validating ${affected_rules[*]}"
echo "Validating ${affected_rules[@]}"
fi
printf '\n\n\n'
# Validate metadata
if [[ "${#affected_rules[@]}" -gt 0 ]]
then
cd rspec-tools
pipenv install
printf '\n\n\n'
pipenv run rspec-tools validate-rules-metadata "${affected_rules[@]}"
else
echo "No rule changed or added"

View File

@ -2,7 +2,7 @@
== In the RSPEC
The rule status (`/status` in the metadata) should be set to `deprecated`, and its tags should be removed. If the rule is in `SonarWay`, it should be removed from this profile.
The rule status (`/status` in the metadata) should be set to `deprecated`.
Optionally, `/extra/replacementRules` can list the rules that replace this rule.

View File

@ -271,24 +271,6 @@ tsql:: use `sql`
In case no language is appropriate for a code block (for example shared examples between multiple languages), you can use `text` as the language.
=== Comments within code blocks
Colon (`:`) should be used as separator between `Noncompliant`/`Compliant` comments and the text explanation that follows, if any.
[source,cpp]
----
int X = 2; // Noncompliant: variable should be in lowercase
----
When referencing a name within a comment in a code example, use double quotes to make it clear it refers to an existing element in the code.
[source,cpp]
----
int i = 0;
cout << noexcept(++i); // Noncompliant: "i" is not incremented
----
=== Diff view
Additionally, you can also use two attributes to let the products know your code examples should be highlighted with a diff view when possible

View File

@ -1,4 +1,3 @@
// Ansible
// C#
* ASP.NET
* ASP.NET Core
@ -9,7 +8,6 @@
* Dapper
* BouncyCastle
* Jwt.Net
* Blazor
// C-Family
* Botan
* CryptoPP
@ -27,7 +25,6 @@
* libxml2
// Java
* Android
* Android WebView
* Apache Commons
* Apache Commons
* Apache Commons Email
@ -43,16 +40,14 @@
* Java EE
* Java JWT
* Java SE
* Java JDBC API
* Java I/O API
* Jdom2
* JSP
* Legacy Mongo Java API
* OkHttp
* Realm
* Java Cryptography Extension
* Apache HttpClient
* Couchbase
* SAX
* Servlet
* Spring
* Spring Data MongoDB
@ -62,13 +57,10 @@
* SQLCipher
* Thymeleaf
// JS
* Jasmine
* Jest
* Flow.js
* Node.js
* Express.js
* SSH2
* Mocha
* MongoDB
* Mongoose
* Sequelize
@ -83,7 +75,6 @@
* TypeScript
* PropTypes
* JSX
* Electron
// PHP
* Core PHP
* Guzzle
@ -98,7 +89,6 @@
* Argon2-cffi
* Bcrypt
* Cryptodome
* databases
* Django
* Django Templates
* FastAPI
@ -119,7 +109,6 @@
* python-ldap
* Python SQLite
* Python Standard Library
* PyTorch
* PyYAML
* Requests
* Scrypt
@ -127,7 +116,6 @@
* SignXML
* SQLAlchemy
* ssl
* TensorFlow
// Docker
* Wget
// Cloudformation
@ -153,7 +141,7 @@
* CryptoSwift
* IDZSwiftCommonCrypto
// Azure resource manager
* JSON templates
* ARM templates
* Bicep
// PL/SQL
* DBMS_CRYPTO
@ -161,5 +149,3 @@
* Go Standard Library
// Kubernetes
* Helm
// Kotlin
Jetpack Compose

View File

@ -45,7 +45,6 @@ When web pages have massively long names like "Java™ Platform, Standard Editio
* AWS blog - https://aws.amazon.com/blogs
* Azure Documentation - https://learn.microsoft.com/en-us/azure/?product=popular
* CERT - https://wiki.sei.cmu.edu/confluence/display/seccode
* Clippy Lints - https://rust-lang.github.io/rust-clippy/master/index.html
* {cpp} reference - https://en.cppreference.com/w/
* {cpp} Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/e49158a/CppCoreGuidelines.md
* CVE - https://cve.mitre.org

View File

@ -40,6 +40,6 @@ You can update the quickfix field using this GitHub Workflow: https://github.com
The code field is an object that contains information related to the clean code taxonomy. It is an object with two required properties:
* `impacts`: A nested object that is treated as a mapping from a software quality to a level (`"INFO"`, `"LOW"`, `"MEDIUM"`, `"HIGH"` or `"BLOCKER"`). Note that at least one software quality has to be specified. The current list of allowed software qualities is `"MAINTAINABILITY"`, `"RELIABILITY"` and `"SECURITY"`.
* `impacts`: A nested object that is treated as a mapping from a software quality to a level (`"LOW"`, `"MEDIUM"` or `"HIGH"`). Note that at least one software quality has to be specified. The current list of allowed software qualities is `"MAINTAINABILITY"`, `"RELIABILITY"` and `"SECURITY"`.
* `attribute`: A single clean code attribute that the rule aims to achieve. This has to be one of the following values: `"FORMATTED"`, `"CONVENTIONAL"`, `"IDENTIFIABLE"`, `"CLEAR"`, `"LOGICAL"`, `"COMPLETE"`, `"EFFICIENT"`, `"FOCUSED"`, `"DISTINCT"`, `"MODULAR"`, `"TESTED"`, `"LAWFUL"`, `"TRUSTWORTHY"`, `"RESPECTFUL"`.

View File

@ -109,17 +109,3 @@ Use it when referencing variable names, file names, tokens, and all kinds of spe
Write:: Compiling source file `src/generic_file.py` breaks an `assert` call in pytest framework.
Avoid:: Compiling source file "src/generic_file.py" breaks an `assert` call in `pytest` framework.
== Referencing elements from the code
When referencing elements from the code within a normal sentence, use the `backticks` (```) to format it. This includes variable names, function names, class names, and so on.
When referencing the same elements within a comment in a code block, surrpond it with double quotes.
[source,cpp]
----
int i = 0;
// Write
cout << noexcept(++i); // Noncompliant, "i" is not incremented -> Double quotes
// Avoid
cout << noexcept(++i); // Noncompliant, i is not incremented -> No quotes
cout << noexcept(++i); // Noncompliant, `i` is not incremented -> Backticks
----

File diff suppressed because it is too large Load Diff

View File

@ -158,24 +158,22 @@ const languageToJiraProject = new Map(Object.entries({
'HTML': 'SONARHTML',
'PHP': 'SONARPHP',
'PLI': 'SONARPLI',
'PLSQL': 'PLSQL',
'PLSQL': 'SONARPLSQL',
'RPG': 'SONARRPG',
'APEX': 'SONARAPEX',
'RUBY': 'SONARRUBY',
'RUST': 'SKUNK',
'APEX': 'SONARSLANG',
'RUBY': 'SONARSLANG',
'KOTLIN': 'SONARKT',
'SCALA': 'SONARSCALA',
'GO': 'SONARGO',
'SECRETS': 'SONARTEXT',
'SCALA': 'SONARSLANG',
'GO': 'SONARSLANG',
'SECRETS': 'SECRETS',
'SWIFT': 'SONARSWIFT',
'TSQL': 'TSQL',
'VB6': 'VB6',
'TSQL': 'SONARTSQL',
'VB6': 'SONARVBSIX',
'XML': 'SONARXML',
'CLOUDFORMATION': 'SONARIAC',
'TERRAFORM': 'SONARIAC',
'KUBERNETES': 'SONARIAC',
'TEXT': 'SONARTEXT',
'ANSIBLE': 'SONARIAC',
}));
const languageToGithubProject = new Map(Object.entries({
@ -189,11 +187,10 @@ const languageToGithubProject = new Map(Object.entries({
'TYPESCRIPT': 'SonarJS',
'SWIFT': 'sonar-swift',
'KOTLIN': 'sonar-kotlin',
'GO': 'sonar-go',
'SCALA': 'sonar-scala',
'RUBY': 'sonar-ruby',
'RUST': 'sonar-rust',
'APEX': 'sonar-apex',
'GO': 'slang-enterprise',
'SCALA': 'slang-enterprise',
'RUBY': 'slang-enterprise',
'APEX': 'slang-enterprise',
'HTML': 'sonar-html',
'COBOL': 'sonar-cobol',
'VB6': 'sonar-vb',
@ -212,9 +209,8 @@ const languageToGithubProject = new Map(Object.entries({
'CLOUDFORMATION': 'sonar-iac',
'TERRAFORM': 'sonar-iac',
'KUBERNETES': 'sonar-iac',
'SECRETS': 'sonar-text',
'SECRETS': 'sonar-secrets',
'TEXT': 'sonar-text',
'ANSIBLE': 'sonar-iac-enterprise',
}));
function ticketsAndImplementationPRsLinks(ruleNumber: string, title: string, language?: string) {
@ -320,7 +316,7 @@ function usePageMetadata(ruleid: string, language: string, classes: UsedStyles):
if (coverage !== 'Not Covered') {
prUrl = undefined;
branch = 'master';
branch = 'master';
}
return {

View File

@ -31,7 +31,7 @@ export async function process_incomplete_rspecs(tmpRepoDir: string,
const repo = await (() => {
if (!fs.existsSync(path.join(tmpRepoDir, '.git'))) {
if (process.env.GITHUB_TOKEN) {
return Git.Clone.clone(`https://${process.env.GITHUB_TOKEN}@github.com/SonarSource/rspec/`, tmpRepoDir);
return Git.Clone.clone('https://' + process.env.GITHUB_TOKEN + '@github.com/SonarSource/rspec/', tmpRepoDir);
} else {
return Git.Clone.clone('https://github.com/SonarSource/rspec/', tmpRepoDir);
}

View File

@ -27,7 +27,6 @@ const languageToSonarpedia = new Map<string, string[]>(Object.entries({
'plsql': ['PLSQL'],
'python': ['PY'],
'rpg': ['RPG'],
'rust': ['RUST'],
'secrets': ['SECRETS'],
'swift': ['SWIFT'],
'tsql': ['TSQL'],
@ -38,8 +37,7 @@ const languageToSonarpedia = new Map<string, string[]>(Object.entries({
'cloudformation': ['CLOUDFORMATION'],
'terraform': ['TERRAFORM'],
'kubernetes': ['KUBERNETES'],
'text': ['TEXT'],
'ansible': ['ANSIBLE']
'text': ['TEXT']
}));
export function useRuleCoverage() {

View File

@ -55,5 +55,5 @@ In order to generate an HTML file from the ASCIIdoc, you can use [asciidoctor](h
[source,sh]
----
$ asciidoctor rule.adoc
$ asciidoctor -e rule.adoc
----

View File

@ -3,11 +3,11 @@
"type": "VULNERABILITY",
"code": {
"impacts": {
"SECURITY": "BLOCKER"
"SECURITY": "HIGH"
},
"attribute": "TRUSTWORTHY"
},
"status": "beta",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "30min"
@ -45,7 +45,7 @@
"3.5.2",
"6.4.1"
],
"STIG ASD_V5R3": [
"STIG ASD 2023-06-08": [
"V-222642"
]
},

View File

@ -1,11 +1,3 @@
:example_env: ENV_VAR_NAME
:example_name: java-property-name
:example_secret: example_secret_value
// Set value that can be used to refer to the type of secret in, for example:
// "An attacker can use this {secret_type} to ..."
// Commonly used values: access token, api key, application secret, application key or consumer key, service password, OAuth token, deployment password
:secret_type: secret
include::../../../shared_content/secrets/description.adoc[]
@ -17,97 +9,32 @@ include::../../../shared_content/secrets/rationale.adoc[]
// Optional: Give a general description of the secret and what it's used for.
include::../../../shared_content/secrets/impact/generic_impact.adoc[]
Below are some real-world scenarios that illustrate some impacts of an attacker
exploiting the secret.
// Uncomment the following line, if specifying detailed impacts from below (also make sure to have new lines around the uncommented includes):
// include::../../../shared_content/secrets/impact/specific_impact_intro.adoc[]
// Set value that can be used to refer to the type of secret in, for example:
// "An attacker can use this {secret_type} to ..."
:secret_type: secret
// Secret may allow hosting arbitrary files
// include::../../../shared_content/secrets/impact/malware_distribution.adoc[]
// Where possible, use predefined content for common impacts. This content can
// be found in the folder "shared_content/secrets/impact".
// When using predefined content, search for any required variables to be set and include them in this file.
// Not adding them will not trigger warnings.
// Secret may allow accessing or compromising sensitive data
// include::../../../shared_content/secrets/impact/data_compromise.adoc[]
// Secret may allow uploading artifacts to services used elsewhere in the supply chain
// This is specific for code and artifact repositories
// include::../../../shared_content/secrets/impact/supply_chain_attack.adoc[]
// Secret may be used to trigger workflows
// This is webhook-specific
// include::../../../shared_content/secrets/impact/codeless_vulnerability_chaining.adoc[]
// OAuth tokens may allow accessing 3rd party services
// include::../../../shared_content/secrets/impact/oauth_token_compromise.adoc[]
// Mailing service compromise may allow sending spam, which may result in account termination
// include::../../../shared_content/secrets/impact/suspicious_activities_termination.adoc[]
// Sensitive information leak / identity impersonation, e.g., through leaked signing secret
// include::../../../shared_content/secrets/impact/security_downgrade.adoc[]
// Audit trail discrepancies
// include::../../../shared_content/secrets/impact/non_repudiation.adoc[]
// Package repository secrets may allow access to source code etc.
// include::../../../shared_content/secrets/impact/source_code_compromise.adoc[]
// Spamming automated calls may cause large bills and rate limited service access
// include::../../../shared_content/secrets/impact/exceed_rate_limits.adoc[]
// For blockchain specific tokens
// include::../../../shared_content/secrets/impact/blockchain_data_exposure.adoc[]
// Specific for banking / financial transaction tokens, causing financial loss
// include::../../../shared_content/secrets/impact/banking_financial_loss.adoc[]
// Secret can be used to send spam or phish users
// include::../../../shared_content/secrets/impact/phishing.adoc[]
// Secret may allow modifying application data (object stores etc.)
// include::../../../shared_content/secrets/impact/data_modification.adoc[]
// Specific to services that are used to share PII (personal infos, chat logs, ..)
// include::../../../shared_content/secrets/impact/personal_data_compromise.adoc[]
// Secret may allow accessing financial data, like CC information or confidential financial reports
// include::../../../shared_content/secrets/impact/disclosure_of_financial_data.adoc[]
// Secret may allow occurring financial losses through 3rd party API usage
// include::../../../shared_content/secrets/impact/financial_loss.adoc[]
// Secret may be used to modify dashboards to corrupt shown data
// Requires setting service_name variable
// :service_name: secret service
// include::../../../shared_content/secrets/impact/dataviz_takeover.adoc[]
// Secret is related to IaaS providers and can be used to change DNS, launch VMs, etc.
// Requires setting service_name variable
// :service_name: secret service
// include::../../../shared_content/secrets/impact/infrastructure_takeover.adoc[]
//include::../../../shared_content/secrets/impact/some_impact.adoc[]
== How to fix it
// 1. Revoke leaked secrets
include::../../../shared_content/secrets/fix/revoke.adoc[]
// 2. Analyze recent use to identify misuse
include::../../../shared_content/secrets/fix/recent_use.adoc[]
// 3. Use a secret vault in the future
include::../../../shared_content/secrets/fix/vault.adoc[]
// 4. Never hard-code secrets
include::../../../shared_content/secrets/fix/default.adoc[]
// OAuth PKCE is very specific to OAuth 2.0
// include::../../../shared_content/secrets/fix/oauth_pkce.adoc[]
=== Code examples
:example_secret: example_secret_value
:example_name: java-property-name
:example_env: ENV_VAR_NAME
include::../../../shared_content/secrets/examples.adoc[]
//=== How does this work?

View File

@ -1,28 +1,21 @@
import datetime
import json
import pathlib
import random
import socket
import os,io
import re
import requests
import json
import random
import datetime
from bs4 import BeautifulSoup
from socket import timeout
import pathlib
TOLERABLE_LINK_DOWNTIME = datetime.timedelta(days=7)
LINK_PROBES_HISTORY_FILE = './link_probes.history'
PROBING_COOLDOWN = datetime.timedelta(days=2)
PROBING_SPREAD = 60 * 24 # in minutes, 1 day
PROBING_COOLDOWN = datetime.timedelta(days=1)
PROBING_SPREAD = 100 # minutes
link_probes_history = {}
# These links consistently fail in CI, but work-on-my-machine
EXCEPTION_PREFIXES = [
# It seems the server certificate was renewed on 2nd of August 2024.
# The server is sending only its certificate, without including the
# Intermediate certificate used to issue the server cert. Because of that
# some application are not able to verify the complete chain of trust.
"https://wiki.sei.cmu.edu/",
# The CI reports 403 on drupal.org while it works locally.
# Maybe the CI's IP is blocklisted...
"https://www.drupal.org/",
EXCEPTIONS = [
]
def show_files(filenames):
@ -72,7 +65,7 @@ def live_url(url: str, timeout=5):
req = requests.Request('GET', url, headers = {'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="90"',
'sec-ch-ua-mobile': '?0',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 GLS/100.10.9939.100',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Sec-Fetch-Site':'none',
'Sec-Fetch-Mode':'navigate',
@ -101,13 +94,13 @@ def live_url(url: str, timeout=5):
print(f"ERROR: Too many redirects: {rr}")
return False
except requests.Timeout as t:
print(f"ERROR: Request timeout {t}")
print(f"ERROR: timeout ", t)
return False
except socket.timeout as t:
print(f"ERROR: Socket timeout {t}")
except timeout as t:
print(f"ERROR: timeout ", t)
return False
except Exception as e:
print(f"ERROR: {e}")
print(f"ERROR: ", e)
return False
def findurl_in_html(filename,urls):
@ -152,51 +145,20 @@ def get_all_links_from_htmls(dir):
print("All html files crawled")
return urls
def url_is_exception(url: str) -> bool:
return any(
url.startswith(e) for e in EXCEPTION_PREFIXES
)
def probe_links(urls: dict) -> bool:
def probe_links(urls):
errors = []
link_cache_exception = 0
link_cache_hit = 0
link_cache_miss = 0
print("Testing links")
link_count = len(urls)
for idx, url in enumerate(urls):
print(f"[{idx+1}/{link_count}] {url} in {len(urls[url])} files")
if url_is_exception(url):
link_cache_exception += 1
for url in urls:
print(f"{url} in {len(urls[url])} files")
if url in EXCEPTIONS:
print("skip as an exception")
elif url_was_reached_recently(url):
link_cache_hit += 1
print("skip probing because it was reached recently")
elif live_url(url, timeout=5):
link_cache_miss += 1
rejuvenate_url(url)
elif url_is_long_dead(url):
link_cache_miss += 1
errors.append(url)
else:
link_cache_miss += 1
confirmed_errors = confirm_errors(errors, urls)
print(f"\n\n\n{'=' * 80}\n\n\n")
if confirmed_errors:
report_errors(confirmed_errors, urls)
print(f"{len(confirmed_errors)}/{len(urls)} links are dead, see above ^^ the list and the related files\n\n")
print("Cache statistics:")
print(f"\t{link_cache_hit=}")
print(f"\t{link_cache_miss=}")
link_cache_hit_ratio = (link_cache_hit) / (link_cache_hit + link_cache_miss)
print(f"\t{link_cache_hit_ratio:03.2%} hits")
print(f"\t{link_cache_exception=}")
print(f"\n\n\n{'=' * 80}\n\n\n")
success = len(confirmed_errors) == 0
return success
return errors
def confirm_errors(presumed_errors, urls):
confirmed_errors = []
@ -218,9 +180,16 @@ def report_errors(errors, urls):
def check_html_links(dir):
load_url_probing_history()
urls = get_all_links_from_htmls(dir)
success = probe_links(urls)
if success:
errors = probe_links(urls)
exit_code = 0
if errors:
confirmed_errors = confirm_errors(errors, urls)
if confirmed_errors:
report_errors(confirmed_errors, urls)
print(f"{len(confirmed_errors)}/{len(urls)} links are dead, see above ^^ the list and the related files")
exit_code = 1
if exit_code == 0:
print(f"All {len(urls)} links are good")
save_url_probing_history()
exit(0 if success else 1)
exit(exit_code)

View File

@ -1,43 +1,14 @@
import collections
import json
import os
import re
import sys
import json
import collections
from git import Repo
from git import Git
from pathlib import Path
from git import Git, Repo
from rspec_tools.utils import load_json, pushd
from rspec_tools.utils import (load_json, pushd)
REPOS = [
'sonar-abap',
'sonar-apex',
'sonar-architecture',
'sonar-cobol',
'sonar-cpp',
'sonar-dart',
'sonar-dataflow-bug-detection',
'sonar-dotnet-enterprise',
'sonar-flex',
'sonar-go-enterprise',
'sonar-html',
'sonar-iac-enterprise',
'sonar-java',
'SonarJS',
'sonar-kotlin',
'sonar-php',
'sonar-pli',
'sonar-plsql',
'sonar-python-enterprise',
'sonar-rpg',
'sonar-ruby',
'sonar-scala',
'sonar-security',
'sonar-swift',
'sonar-text-enterprise',
'sonar-tsql',
'sonar-vb',
'sonar-xml'
]
REPOS = ['sonar-abap','sonar-cpp','sonar-cobol','sonar-dotnet','sonar-css','sonar-flex','slang-enterprise','sonar-java','SonarJS','sonar-php','sonar-pli','sonar-plsql','sonar-python','sonar-rpg','sonar-swift','sonar-text','sonar-tsql','sonar-vb','sonar-html','sonar-xml','sonar-kotlin', 'sonar-secrets', 'sonar-security', 'sonar-dataflow-bug-detection', 'sonar-iac']
CANONICAL_NAMES = {
'CLOUD_FORMATION': 'CLOUDFORMATION',
@ -46,14 +17,14 @@ CANONICAL_NAMES = {
'WEB': 'HTML'
}
RULES_FILENAME = 'covered_rules.json'
def get_rule_id(filename):
rule_id = filename[:-5]
return rule_id.removesuffix('_abap').removesuffix('_java')
if '_' in rule_id:
return rule_id[:rule_id.find('_')]
else:
return rule_id
def compatible_languages(rule, languages_from_sonarpedia):
'''
@ -72,7 +43,7 @@ def get_implemented_rules(path, languages_from_sonarpedia):
for lang in languages_from_sonarpedia:
implemented_rules[lang] = []
for filename in os.listdir(path):
if filename.endswith(".json") and 'profile' not in filename:
if filename.endswith(".json") and not filename.startswith("Sonar_way"):
rule = load_json(os.path.join(path, filename))
rule_id = get_rule_id(filename)
for language in compatible_languages(rule, languages_from_sonarpedia):
@ -184,29 +155,17 @@ def checkout_repo(repo):
git_url=f"https://github.com/SonarSource/{repo}"
token=os.getenv('GITHUB_TOKEN')
if token:
git_url=f"https://oauth2:{token}@github.com/SonarSource/{repo}"
git_url=f"https://{token}@github.com/SonarSource/{repo}"
if not os.path.exists(repo):
return Repo.clone_from(git_url, repo)
else:
return Repo(repo)
VERSION_RE = re.compile(r'\d[\d\.]+')
def is_version_tag(name):
return bool(re.fullmatch(VERSION_RE, name))
def comparable_version(key):
if not is_version_tag(key):
return [0]
return list(map(int, key.split('.')))
def collect_coverage_for_all_versions(repo, coverage):
git_repo = checkout_repo(repo)
tags = git_repo.tags
versions = [tag.name for tag in tags if is_version_tag(tag.name)]
versions.sort(key = comparable_version)
tags.sort(key = lambda t: t.commit.committed_date)
versions = [tag.name for tag in tags if '-' not in tag.name]
for version in versions:
collect_coverage_for_version(repo, git_repo, version, coverage)
collect_coverage_for_version(repo, git_repo, 'master', coverage)

View File

@ -19,7 +19,7 @@ LANG_TO_LABEL = {'abap': 'abap',
'dart': 'dart',
'docker': 'iac',
'flex': 'flex',
'go': 'go',
'go': 'slang',
'html': 'html',
'java': 'java',
'javascript': 'jsts',
@ -40,7 +40,6 @@ LANG_TO_LABEL = {'abap': 'abap',
'tsql': 'tsql',
'vb6': 'vb6',
'vbnet': 'dotnet',
'ansible': 'iac',
'cloudformation': 'iac',
'terraform': 'iac',
'kubernetes': 'iac',
@ -77,7 +76,6 @@ LANG_TO_SOURCE = {
'c': 'c',
'objectivec': 'objectivec',
'vb': 'vb',
'ansible': 'yaml',
# these languages are not supported by highlight.js as the moment:
'apex': 'apex',
'azureresourcemanager': 'bicep',

View File

@ -1,6 +1,6 @@
import re
from pathlib import Path
from typing import Dict, Final, List, Union
from typing import Final, Dict, List
from bs4 import BeautifulSoup
from rspec_tools.errors import RuleValidationError
@ -78,26 +78,15 @@ def intersection(list1, list2):
def difference(list1, list2):
return list(set(list1) - set(list2))
def validate_titles_are_not_misclassified_as_subtitles(rule_language: LanguageSpecificRule, subtitles: list[str], allowed_h2_sections: list[str]):
# TODO This does not validate "How to fix it" section for frameworks as the section names are a bit special.
misclassified = intersection(subtitles, allowed_h2_sections)
if misclassified:
misclassified.sort()
misclassified_str = ', '.join(misclassified)
raise RuleValidationError(f'Rule {rule_language.id} has some sections misclassified. Ensure there are not too many `=` in the asciidoc file for: {misclassified_str}')
def validate_section_names(rule_language: LanguageSpecificRule):
"""Validates all h2-level section names"""
def get_titles(level: Union[str, list[str]]) -> list[str]:
return list(map(lambda x: x.text.strip(), rule_language.description.find_all(level)))
h2_titles = get_titles('h2')
subtitles = get_titles(['h3', 'h4', 'h5', 'h6'])
allowed_h2_sections = list(MANDATORY_SECTIONS) + list(OPTIONAL_SECTIONS.keys())
validate_titles_are_not_misclassified_as_subtitles(rule_language, subtitles, allowed_h2_sections)
descr = rule_language.description
h2_titles = list(map(lambda x: x.text.strip(), descr.find_all('h2')))
validate_duplications(h2_titles, rule_language)
education_titles = intersection(h2_titles, allowed_h2_sections)
education_titles = intersection(h2_titles, list(MANDATORY_SECTIONS) + list(OPTIONAL_SECTIONS.keys()))
if education_titles:
# Using the education format.
validate_how_to_fix_it_sections_names(rule_language, h2_titles)
@ -245,7 +234,7 @@ def validate_security_standard_links(rule_language: LanguageSpecificRule):
# Avoid raising mismatch issues on deprecated or closed rules
if metadata.get('status') != 'ready':
return
security_standards_metadata = metadata.get('securityStandards', {})
for standard in SECURITY_STANDARD_URL.keys():
@ -255,7 +244,7 @@ def validate_security_standard_links(rule_language: LanguageSpecificRule):
extra_links = difference(links_mapping, metadata_mapping)
if len(extra_links) > 0:
raise RuleValidationError(f'Rule {rule_language.id} has a mismatch for the {standard} security standards. Remove links from the Resources/See section ({extra_links}) or fix the rule metadata')
missing_links = difference(metadata_mapping, links_mapping)
if len(missing_links) > 0:
raise RuleValidationError(f'Rule {rule_language.id} has a mismatch for the {standard} security standards. Add links to the Resources/See section ({missing_links}) or fix the rule metadata')

View File

@ -156,15 +156,6 @@
},
"uniqueItems": true
},
"OWASP Mobile Top 10 2024": {
"type": "array",
"minItems": 0,
"items": {
"type": "string",
"pattern": "^M([1-9]|10)$"
},
"uniqueItems": true
},
"PCI DSS 3.2": {
"type": "array",
"minItems": 0,
@ -228,7 +219,7 @@
},
"uniqueItems": true
},
"STIG ASD_V5R3": {
"STIG ASD 2023-06-08": {
"type": "array",
"minItems": 0,
"items": {
@ -276,15 +267,15 @@
"properties": {
"MAINTAINABILITY": {
"type": "string",
"enum": ["INFO", "LOW", "MEDIUM", "HIGH", "BLOCKER"]
"enum": ["LOW", "MEDIUM", "HIGH"]
},
"RELIABILITY": {
"type": "string",
"enum": ["INFO", "LOW", "MEDIUM", "HIGH", "BLOCKER"]
"enum": ["LOW", "MEDIUM", "HIGH"]
},
"SECURITY": {
"type": "string",
"enum": ["INFO", "LOW", "MEDIUM", "HIGH", "BLOCKER"]
"enum": ["LOW", "MEDIUM", "HIGH"]
}
}
},

View File

@ -1,6 +0,0 @@
{
"title": "Function names should comply with a naming convention",
"defaultQualityProfiles": [
]
}

View File

@ -1,5 +0,0 @@
== Why is this an issue?
=== How to fix it
=== Resources

View File

@ -1,457 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.8">
<title>Why is this an issue?</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Uncomment @import statement below to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite::before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
*:not(pre)>code.nobreak{word-wrap:normal}
*:not(pre)>code.nowrap{white-space:nowrap}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
.literalblock pre.nowrap,.literalblock pre.nowrap pre,.listingblock pre.nowrap,.listingblock pre.nowrap pre{white-space:pre;word-wrap:normal}
.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #dddddf}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt,.quoteblock .quoteblock{margin:0 0 1.25em;padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0}
table.tableblock{max-width:100%;border-collapse:separate}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}
table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}
table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}
table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px}
table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0}
table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}
table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot,table.frame-ends{border-width:1px 0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd){background:#f8f8f7}
table.stripes-none tr,table.stripes-odd tr:nth-of-type(even){background:none}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
</head>
<body class="article">
<div id="header">
</div>
<div id="content">
<div class="sect1">
<h2 id="_why_is_this_an_issue">Why is this an issue?</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_how_to_fix_it">How to fix it</h3>
</div>
<div class="sect2">
<h3 id="_resources">Resources</h3>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2024-10-10 12:53:12 UTC
</div>
</div>
</body>
</html>

View File

@ -39,11 +39,6 @@ def test_unexpected_section_fails_validation(invalid_rule):
with pytest.raises(RuleValidationError, match=fr'^Rule {rule.id} has an unconventional header "Invalid header"'):
validate_section_names(rule)
def test_sections_with_wrong_level_fails_validation(invalid_rule):
rule = invalid_rule('S100', 'php')
with pytest.raises(RuleValidationError, match=fr'^Rule {rule.id} has some sections misclassified. Ensure there are not too many `=` in the asciidoc file for: How to fix it, Resources'):
validate_section_names(rule)
def test_valid_section_levels_passes_validation(rule_language):
'''Check that description with correct formatting is considered valid.'''
validate_section_levels(rule_language('S100', 'cfamily'))

View File

@ -82,7 +82,7 @@ def test_rule_with_invalid_impacts(invalid_rules: RulesRepository):
def test_rule_with_invalid_impact_level(invalid_rules: RulesRepository):
s506 = invalid_rules.get_rule('S506')
with pytest.raises(RuleValidationError, match=re.escape("Rule S506 failed validation for these reasons:\n - Rule scala:S506 has invalid metadata in MAINTAINABILITY: 'INVALID' is not one of ['INFO', 'LOW', 'MEDIUM', 'HIGH', 'BLOCKER']")):
with pytest.raises(RuleValidationError, match=re.escape("Rule S506 failed validation for these reasons:\n - Rule scala:S506 has invalid metadata in MAINTAINABILITY: 'INVALID' is not one of ['LOW', 'MEDIUM', 'HIGH']")):
validate_rule_metadata(s506)

View File

@ -1,9 +1,5 @@
{
"title": "Function and method names should comply with a naming convention",
"defaultQualityProfiles": [],
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"scope": "Main"
}

View File

@ -2,7 +2,7 @@ include::../rule.adoc[]
For example, with the default provided regular expression ``++^[a-z][a-zA-Z0-9]*$++``, the function:
[source,php,diff-id=1,diff-type=noncompliant]
[source,php]
----
function DoSomething(){ // Noncompliant
// ...
@ -11,31 +11,13 @@ function DoSomething(){ // Noncompliant
should be renamed to
[source,php,diff-id=1,diff-type=compliant]
[source,php]
----
function doSomething(){
// ...
}
----
In case the Drupal framework is detected and the default regex is not replaced, it will follow the PHP coding standards for Drupal.
[source,php,diff-id=2,diff-type=noncompliant]
----
function doSomething(){ // Noncompliant
// ...
}
----
should be renamed to
[source,php,diff-id=2,diff-type=compliant]
----
function do_something(){
// ...
}
----
=== Exceptions
Methods with an ``++@inheritdoc++`` annotation, as well as magic methods (``++__construct()++``, ``++__destruct()++``, ``++__call()++``, ``++__callStatic()++``, ``++__get()++``, ``++__set()++``, ``++__isset()++``, ``++__unset()++``, ``++__sleep()++``, ``++__wakeup()++``, ``++__toString()++``, ``++__invoke()++``, ``++__set_state()++``, ``++__clone()++``, ``++__debugInfo()++``) are ignored.
@ -52,10 +34,6 @@ function __destruct(){...} // Compliant by exception
function myFunc(){...} // Compliant by exception
----
== References
* https://www.drupal.org/docs/develop/standards/php/php-coding-standards#s-functions-and-variables[Drupal - Naming Conventions - Functions and variables]
ifdef::env-github,rspecator-view[]
'''

View File

@ -1,6 +1,6 @@
== Why is this an issue?
Shared naming conventions allow teams to collaborate efficiently. In Dart, the convention is that all type names should be in camel-case starting with a capital letter (aka Pascal case).
Shared naming conventions allow teams to collaborate efficiently. In Dart the convention is that all type names should be in camel-case starting with a capital letter (aka Pascal case).
This rule raises an issue when a class name does not comply with this convention.
@ -12,45 +12,18 @@ This rule raises an issue when a class name does not comply with this convention
[source,dart]
----
class My_Class { } // Noncompliant: contains a dash
class myClass { } // Noncompliant: starts with a lowercase
class myclass { } // Noncompliant: all in lowercase
class My_Class // Noncompliant,contains dash
class myClass // Noncompliant, starts with lovercase
class myclass // Noncompliant, all in lowercase
----
==== Compliant solution
[source,dart]
----
class MyClass { }
class MyClass
----
== Resources
* Dart Docs - https://dart.dev/tools/linter-rules/camel_case_types[Dart Linter rule - camel_case_types]
=== Related rules
* S7046 - Extension identifiers should comply with a naming convention
* S7075 - Non-constant names should comply with a naming convention
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
* The type name '<typeName>' isn't an UpperCamelCase identifier.
=== Highlighting
The type name (without generic parameters, or representation type declaration).
'''
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
endif::env-github,rspecator-view[]
* https://dart.dev/tools/linter-rules/camel_case_types[Dart Lint rule]

View File

@ -2,9 +2,5 @@
"defaultQualityProfiles": [
"Sonar way"
],
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"scope": "Main"
}

View File

@ -2,9 +2,5 @@
"scope": "Main",
"defaultQualityProfiles": [],
"status": "deprecated",
"tags": [],
"compatibleLanguages": [
"js",
"ts"
]
}
"tags": []
}

View File

@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "BLOCKER"
"MAINTAINABILITY": "HIGH"
},
"attribute": "CLEAR"
},
@ -17,13 +17,19 @@
"based-on-misra"
],
"extra": {
"replacementRules": [],
"legacyKeys": []
"replacementRules": [
],
"legacyKeys": [
]
},
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-1032",
"sqKey": "S1032",
"scope": "Main",
"defaultQualityProfiles": [],
"defaultQualityProfiles": [
],
"quickfix": "unknown"
}

View File

@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "BLOCKER"
"MAINTAINABILITY": "HIGH"
},
"attribute": "CLEAR"
},
@ -19,8 +19,12 @@
"pitfall"
],
"extra": {
"replacementRules": [],
"legacyKeys": []
"replacementRules": [
],
"legacyKeys": [
]
},
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-1036",

View File

@ -1,7 +1,3 @@
{
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"scope": "Main"
}

View File

@ -1,6 +1,3 @@
{
"title": "All \"except\" blocks should be able to catch exceptions",
"tags": [
"suspicious"
]
"title": "All \"except\" blocks should be able to catch exceptions"
}

View File

@ -2,7 +2,7 @@
"title": "Finalizers should not throw exceptions",
"code": {
"impacts": {
"RELIABILITY": "BLOCKER"
"RELIABILITY": "HIGH"
},
"attribute": "LOGICAL"
},

View File

@ -2,7 +2,7 @@
"title": "Finalize method should not throw exceptions",
"code": {
"impacts": {
"RELIABILITY": "BLOCKER"
"RELIABILITY": "HIGH"
},
"attribute": "LOGICAL"
},

View File

@ -18,8 +18,8 @@ void foo () // no exceptions specified
throw ( 21 ); // anything can be thrown
}
void goo ( ) throw ( Exception )
{
void goo ( ) throw ( Exception )
{
foo ( ); // Noncompliant; an int could be thrown
}
----
@ -34,8 +34,8 @@ void foo () // no exceptions specified
throw ( 21 ); // this is legal; anything can be thrown
}
void goo ( ) throw ( Exception, int )
{
void goo ( ) throw ( Exception, int )
{
foo ( );
}
----
@ -45,6 +45,7 @@ void goo ( ) throw ( Exception, int )
* MISRA {cpp}:2008, 15-5-2
* CWE - https://cwe.mitre.org/data/definitions/391[CWE-391 - Unchecked Error Condition]
* https://www.securecoding.cert.org/confluence/x/EADTAQ[CERT, ERR55-CPP.] - Honor exception specifications
ifdef::env-github,rspecator-view[]

View File

@ -2,9 +2,5 @@
"scope": "Main",
"defaultQualityProfiles": [],
"status": "deprecated",
"tags": [],
"compatibleLanguages": [
"js",
"ts"
]
}
"tags": []
}

View File

@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "BLOCKER"
"RELIABILITY": "HIGH"
},
"attribute": "COMPLETE"
},

View File

@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "BLOCKER"
"RELIABILITY": "HIGH"
},
"attribute": "LOGICAL"
},
@ -12,9 +12,13 @@
"func": "Constant\/Issue",
"constantCost": "30min"
},
"tags": [],
"tags": [
],
"extra": {
"replacementRules": [],
"replacementRules": [
],
"legacyKeys": [
"PipelinedFunctionsWithoutPipeRow"
]

View File

@ -1,5 +1,3 @@
{
"defaultQualityProfiles": [
]
}

View File

@ -1,4 +1,4 @@
:language_std_outputs: print
:language_std_outputs: std::cout, std::cerr, printf, std::print
== Why is this an issue?
@ -8,9 +8,9 @@ If you are using Flutter, you can use `debugPrint` or surround print calls with
=== Code examples
==== Noncompliant code example
The following noncompliant code:
[source,dart]
[source,dart,diff-id=1,diff-type=noncompliant]
----
void doSomething(int x) {
// ...
@ -19,11 +19,12 @@ void doSomething(int x) {
}
----
==== Compliant solution
Could be replaced by:
[source,dart]
[source,dart,diff-id=1,diff-type=compliant]
----
void doSomething(int x) {
void doSomething(int x)
{
// ...
debugPrint('debug: $x');
// ...
@ -32,9 +33,10 @@ void doSomething(int x) {
or
[source,dart]
[source,dart,diff-id=1,diff-type=compliant]
----
void doSomething(int x) {
void doSomething(int x)
{
// ...
if (kDebugMode) {
print('debug: $x');
@ -45,9 +47,10 @@ void doSomething(int x) {
or
[source,dart]
[source,dart,diff-id=1,diff-type=compliant]
----
void doSomething(int x) {
void doSomething(int x)
{
// ...
log('log: $x');
// ...
@ -58,27 +61,4 @@ void doSomething(int x) {
* OWASP - https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/[Top 10 2021 Category A9 - Security Logging and Monitoring Failures]
* OWASP - https://owasp.org/www-project-top-ten/2017/A3_2017-Sensitive_Data_Exposure[Top 10 2017 Category A3 - Sensitive Data Exposure]
* Dart Docs - https://dart.dev/tools/linter-rules/avoid_print[Dart Linter rule - avoid_print]
* Flutter API Docs - https://api.flutter.dev/flutter/foundation/kDebugMode-constant.html[kDebugMode top-level constant]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
* Don't invoke 'print' in production code.
=== Highlighting
The identifier of the `print` method, without argument list.
'''
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
endif::env-github,rspecator-view[]
* Dart Linter - https://dart.dev/tools/linter-rules/avoid_print[Dart Linter - avoid_print]

View File

@ -3,9 +3,7 @@
"bad-practice",
"user-experience"
],
"defaultQualityProfiles": [],
"compatibleLanguages": [
"js",
"ts"
"defaultQualityProfiles": [
]
}
}

View File

@ -1,7 +0,0 @@
{
"defaultQualityProfiles": [],
"tags": [
"bad-practice",
"clippy"
]
}

View File

@ -1,39 +0,0 @@
:language_std_outputs: print!, println!
== Why is this an issue?
include::../description.adoc[]
=== Code examples
==== Noncompliant code example
[source,rust,diff-id=1,diff-type=noncompliant]
----
fn do_something() {
println!("my message"); // Noncompliant, output directly to stdout without a logger
}
----
==== Compliant solution
[source,rust,diff-id=1,diff-type=compliant]
----
use log::{info, LevelFilter};
use simple_logger::SimpleLogger;
fn do_something() {
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
// ...
info!("my message"); // Compliant, output via logger
// ...
}
----
== Resources
=== Documentation
* Clippy Lints - https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
* OWASP - https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/[Top 10 2021 Category A9 - Security Logging and Monitoring Failures]
* OWASP - https://owasp.org/www-project-top-ten/2017/A3_2017-Sensitive_Data_Exposure[Top 10 2017 Category A3 - Sensitive Data Exposure]

View File

@ -4,7 +4,7 @@ include::../description.adoc[]
=== Noncompliant code example
[source,dart,diff-id=1,diff-type=noncompliant]
[source,dart]
----
void foo() {
outer: //label is not used.
@ -16,7 +16,7 @@ void foo() {
=== Compliant solution
[source,dart,diff-id=1,diff-type=compliant]
[source,dart]
----
void foo() {
for(int i = 0; i < 10; i++) {
@ -27,31 +27,5 @@ void foo() {
== Resources
=== Documentation
* CERT - https://wiki.sei.cmu.edu/confluence/x/5dUxBQ[CERT, MSC12-C.] - Detect and remove code that has no effect or is never executed
* Dart Docs - https://dart.dev/tools/diagnostic-messages?utm_source=dartdev&utm_medium=redir&utm_id=diagcode&utm_content=unnecessary_cast#unused_label[Dart Linter rule - unused_label]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
* The label '<labelName>' isn't used.
`<labelName>` doesn't include the semicolon.
=== Highlighting
The label name, including the semicolon.
'''
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
endif::env-github,rspecator-view[]
* https://wiki.sei.cmu.edu/confluence/x/5dUxBQ[CERT, MSC12-C.] - Detect and remove code that has no effect or is never executed
* https://dart.dev/tools/diagnostic-messages?utm_source=dartdev&utm_medium=redir&utm_id=diagcode&utm_content=unnecessary_cast#unused_label[Dart Linter - unused label]

View File

@ -1,8 +1,4 @@
{
"defaultQualityProfiles": [],
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"scope": "Main"
}

View File

@ -1,7 +1,3 @@
{
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"scope": "Main"
}

View File

@ -1,8 +1,4 @@
{
"title": "Unused private class members should be removed",
"quickfix": "covered",
"compatibleLanguages": [
"js",
"ts"
]
"title": "Unused private class members should be removed",
"quickfix": "covered"
}

View File

@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
"MAINTAINABILITY": "BLOCKER"
"MAINTAINABILITY": "HIGH"
},
"attribute": "CONVENTIONAL"
},
@ -17,8 +17,12 @@
"sql"
],
"extra": {
"replacementRules": [],
"legacyKeys": []
"replacementRules": [
],
"legacyKeys": [
]
},
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-1069",

View File

@ -1,6 +0,0 @@
[source,dart]
----
void setCoordinates(int x1, int y1, int z1, int x2, int y2, int z2) { // Noncompliant
// ...
}
----

View File

@ -1,21 +0,0 @@
:language: dart
include::../rule.adoc[]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
include::../message.adoc[]
include::../parameters.adoc[]
'''
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
endif::env-github,rspecator-view[]

View File

@ -1,11 +0,0 @@
[source,dart]
----
// Each function does a part of what the original setCoordinates function was doing, so confusion risks are lower
void setOrigin(int x, int y, int z) {
// ...
}
void setSize(int width, int height, int depth) {
// ...
}
----

View File

@ -1,13 +0,0 @@
[source,dart]
----
class Point { // In geometry, Point is a logical structure to group data
int x;
int y;
Point(this.x, this.y);
}
void setCoordinates(Point p1, Point p2) {
// ...
}
----

View File

@ -2,9 +2,5 @@
"defaultQualityProfiles": [
"Sonar way"
],
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"scope": "Main"
}

View File

@ -1,6 +0,0 @@
{
"tags": [
"brain-overload",
"clippy"
]
}

View File

@ -1,6 +0,0 @@
[source,rust]
----
fn set_coordinates(x1: i32, y1: i32, z1: i32, x2: i32, y2: i32, z2: i32) { // Noncompliant
// ...
}
----

View File

@ -1,27 +0,0 @@
:language: rust
include::../rule.adoc[]
== Resources
=== Documentation
* Clippy Lints - https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
include::../message.adoc[]
include::../parameters.adoc[]
'''
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
endif::env-github,rspecator-view[]

View File

@ -1,11 +0,0 @@
[source,rust]
----
// Each function does a part of what the original setCoordinates function was doing, so confusion risks are lower
fn set_origin(x: i32, y: i32, z: i32) {
// ...
}
fn set_size(width: i32, height: i32, depth: i32) {
// ...
}
----

View File

@ -1,17 +0,0 @@
[source,rust]
----
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32) -> Point {
Point { x, y }
}
}
fn set_coordinates(p1: &mut Point, p2: &Point) {
// ...
}
----

View File

@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "BLOCKER"
"RELIABILITY": "HIGH"
},
"attribute": "COMPLETE"
},
@ -12,9 +12,13 @@
"func": "Constant\/Issue",
"constantCost": "1h"
},
"tags": [],
"tags": [
],
"extra": {
"replacementRules": [],
"replacementRules": [
],
"legacyKeys": [
"ForallStatementShouldUseSaveExceptionsClause"
]

View File

@ -1,13 +1,5 @@
include::../description.adoc[]
=== Exceptions
This rule does not raise an issue when:
* A constant path is relative and contains at most two parts.
* A constant path is used in an annotation
* A path is annotated
== How to fix it
=== Code examples
@ -17,8 +9,6 @@ This rule does not raise an issue when:
[source,java,diff-id=1,diff-type=noncompliant]
----
public class Foo {
public static final String FRIENDS_ENDPOINT = "/user/friends"; // Compliant path is relative and has only two parts
public Collection<User> listUsers() {
File userList = new File("/home/mylogin/Dev/users.txt"); // Noncompliant
Collection<User> users = parse(userList);
@ -48,24 +38,6 @@ public class Foo {
}
----
Exceptions examples:
[source,java]
----
public class Foo {
public static final String FRIENDS_ENDPOINT = "/user/friends"; // Compliant path is relative and has only two parts
public static final String ACCOUNT = "/account/group/list.html"; // Compliant path is used in an annotation
@Value("${base.url}" + ACCOUNT)
private String groupUrl;
@MyAnnotation()
String path = "/default/url/for/site"; // Compliant path is annotated
}
----
ifdef::env-github,rspecator-view[]
'''

View File

@ -3,9 +3,5 @@
"accessibility",
"wcag2-a",
"react"
],
"compatibleLanguages": [
"js",
"ts"
]
}
}

View File

@ -46,7 +46,7 @@
"14.1.2",
"5.4.1"
],
"STIG ASD_V5R3": [
"STIG ASD 2023-06-08": [
"V-222612"
]
},

View File

@ -1,8 +1,4 @@
{
"scope": "Main",
"quickfix": "covered",
"compatibleLanguages": [
"js",
"ts"
]
}
"quickfix": "covered"
}

View File

@ -42,7 +42,7 @@
"CERT": [
"STR07-C."
],
"STIG ASD_V5R3": [
"STIG ASD 2023-06-08": [
"V-222612"
]
},

View File

@ -1,31 +1,25 @@
{
"title": "Mouse events should have corresponding keyboard events",
"type": "BUG",
"status": "ready",
"remediation": {
"func": "Constant/Issue",
"constantCost": "5min"
},
"tags": [
"accessibility",
"react"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1082",
"sqKey": "S1082",
"scope": "Main",
"defaultQualityProfiles": [
"Sonar way"
],
"quickfix": "unknown",
"code": {
"impacts": {
"RELIABILITY": "LOW"
"title": "Mouse events should have corresponding keyboard events",
"type": "BUG",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"attribute": "COMPLETE"
},
"compatibleLanguages": [
"js",
"ts"
]
}
"tags": [
"accessibility",
"react"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1082",
"sqKey": "S1082",
"scope": "Main",
"defaultQualityProfiles": ["Sonar way"],
"quickfix": "unknown",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "COMPLETE"
}
}

View File

@ -1,6 +1,3 @@
{
"compatibleLanguages": [
"js",
"ts"
]
}
}

View File

@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant/Issue",
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
@ -14,18 +14,12 @@
"ruleSpecification": "RSPEC-1090",
"sqKey": "S1090",
"scope": "All",
"defaultQualityProfiles": [
"Sonar way"
],
"defaultQualityProfiles": ["Sonar way"],
"quickfix": "infeasible",
"code": {
"impacts": {
"RELIABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"compatibleLanguages": [
"js",
"ts"
]
}
}
}

View File

@ -2,9 +2,5 @@
"scope": "Main",
"defaultQualityProfiles": [],
"status": "deprecated",
"tags": [],
"compatibleLanguages": [
"js",
"ts"
]
}
"tags": []
}

View File

@ -3,9 +3,5 @@
"quickfix": "covered",
"defaultQualityProfiles": [],
"status": "deprecated",
"tags": [],
"compatibleLanguages": [
"js",
"ts"
]
}
"tags": []
}

View File

@ -9,28 +9,18 @@ include::../description.adoc[]
[source,dart,diff-id=1,diff-type=noncompliant]
----
void doSomething() {
; // Noncompliant - was used as a kind of TODO marker
; // Noncompliant - was used as a kind of TODO marker
}
----
[source,dart,diff-id=2,diff-type=noncompliant]
----
void f() {
if (complicated.expression.foo()); // Noncompliant - the condition doesn't apply to bar
if (complicated.expression.foo()); // Noncompliant - the condition doesn't make sense
bar();
}
----
[source,dart,diff-id=3,diff-type=noncompliant]
----
void f() {
if (complicated.expression.foo())
bar();
else ; // Noncompliant else is empty
buzz();
}
----
==== Compliant solution
[source,dart,diff-id=1,diff-type=compliant]
@ -48,38 +38,6 @@ void f() {
}
----
[source,dart,diff-id=3,diff-type=compliant]
----
void f() {
if (complicated.expression.foo())
bar();
else
buzz();
}
----
== Resources
* Dart Docs - https://dart.dev/tools/linter-rules/empty_statements[Dart Linter rule - empty_statements]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
* Unnecessary empty statement.
=== Highlighting
The `;` character.
'''
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
endif::env-github,rspecator-view[]
* https://dart.dev/tools/linter-rules/empty_statements[Dart Lint rule]

View File

@ -4,9 +4,5 @@
"quickfix": "covered",
"defaultQualityProfiles": [],
"status": "deprecated",
"tags": [],
"compatibleLanguages": [
"js",
"ts"
]
}
"tags": []
}

View File

@ -1,6 +0,0 @@
{
"tags": [
"unused",
"clippy"
]
}

View File

@ -1,49 +0,0 @@
== Why is this an issue?
include::../description.adoc[]
=== Code examples
==== Noncompliant code example
[source,rust,diff-id=1,diff-type=noncompliant]
----
fn main() {
let x = 5;
if x > 0 {
println!("x is positive");
}; // Noncompliant
match x {
1 => println!("x is one"),
2 => println!("x is two"),
_ => println!("x is something else"),
}; // Noncompliant
}
----
==== Compliant solution
[source,rust,diff-id=1,diff-type=compliant]
----
fn main() {
let x = 5;
if x > 0 {
println!("x is positive");
}
match x {
1 => println!("x is one"),
2 => println!("x is two"),
_ => println!("x is something else"),
}
}
----
== Resources
=== Documentation
* Clippy Lints - https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon

View File

@ -1,9 +1,6 @@
{
"title": "Variables should not be shadowed",
"defaultQualityProfiles": [],
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"defaultQualityProfiles": [
],
"scope": "Main"
}

View File

@ -1,3 +0,0 @@
{
}

View File

@ -1,53 +0,0 @@
== Why is this an issue?
include::../description.adoc[]
include::../howtofix.adoc[]
=== Code examples
==== Noncompliant code example
[source,dart,diff-id=1,diff-type=noncompliant]
----
class StringUtils { // Noncompliant
static String concatenate(String s1, String s2) {
return s1 + s2;
}
}
----
==== Compliant solution
[source,dart,diff-id=1,diff-type=compliant]
----
class StringUtils { // Compliant
StringUtils._() {
throw Exception('Utility class');
}
static String concatenate(String s1, String s2) {
return s1 + s2;
}
}
----
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Hide this public constructor.
Add a private constructor to hide the implicit public one.
'''
endif::env-github,rspecator-view[]

View File

@ -2,9 +2,5 @@
"defaultQualityProfiles": [
"Sonar way"
],
"scope": "Main",
"compatibleLanguages": [
"js",
"ts"
]
}
"scope": "Main"
}

View File

@ -36,5 +36,5 @@
"defaultQualityProfiles": [
"Sonar way"
],
"quickfix": "infeasible"
"quickfix": "unknown"
}

Some files were not shown because too many files have changed in this diff Show More