diff --git a/.github/workflows/jira-evidence-example.yml b/.github/workflows/jira-evidence-example.yml new file mode 100644 index 0000000..c4d1244 --- /dev/null +++ b/.github/workflows/jira-evidence-example.yml @@ -0,0 +1,100 @@ +name: jira-evidence-example + +on: + workflow_dispatch: # This allows manual triggering of the workflow + push: + branches: + - CCS-2-Additional_evidence_examples + pull_request: + branches: + - CCS-2-Additional_evidence_examples +permissions: + id-token: write + contents: read + +jobs: + docker-build-with-jira-evidence: + runs-on: ubuntu-latest + env: + DOCKER_REPO: 'test-docker-local' + IMAGE_NAME: 'my-very-cool-image:${{ github.run_number }}' + steps: + - name: Install jfrog cli + id: setup-cli + uses: jfrog/setup-jfrog-cli@v4 + env: + JF_URL: ${{ vars.ARTIFACTORY_URL }} + with: + oidc-provider-name: jfrog-github-oidc + + - uses: actions/checkout@v4 + - name: Log in to Artifactory Docker Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.ARTIFACTORY_URL }} + username: ${{ steps.setup-cli.outputs.oidc-user }} + password: ${{ steps.setup-cli.outputs.oidc-token }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and Push Docker image + uses: docker/build-push-action@v6 + id: docker-build + with: + push: true + provenance: false + platforms: linux/amd64 #, linux/arm64 + build-args: REPO_URL=${{ vars.JF_URL }}/example-project-docker-dev-remote + tags: ${{ vars.JF_URL }}/${{ env.DOCKER_REPO }}/${{ env.IMAGE_NAME }} + + - name: add docker package to build + run: | + echo "${{ vars.JF_URL }}/${{ env.DOCKER_REPO }}/${{ env.IMAGE_NAME }}@${{ steps.docker-build.outputs.digest }}" > metadata.json + jf rt build-docker-create ${{ env.DOCKER_REPO }} --image-file metadata.json --build-name $GITHUB_WORKFLOW --build-number ${{ github.run_number }} + + - name: Publish build info + if: ${{ true }} + run: | + jf rt build-collect-env + jf rt build-add-git + jf rt build-publish + + - name: Create JIRA evidence + env: + jira_token: ${{ secrets.JIRA_TOKEN }} + jira_username: ${{ secrets.JIRA_USERNAME }} + jira_url: ${{ secrets.JIRA_URL }} + run: | + BRANCH_NAME=$(git branch --show-current) + jira_id=$(echo "$BRANCH_NAME" | sed -E 's/^([^-]+-[0-9]+).*/\1/') + echo "The branch name is: $BRANCH_NAME" + echo "The jira_id is: $jira_id" + # uncomment the line below to use the commit message instead of the branch name + #START_COMMIT=$(git log -1 --format="%H %s") + #jira_id=$(echo "$BRANCH_NAME" | cut -d' ' -f2) + + # Check if the jira_id matches the JIRA ID format + if [[ $jira_id =~ ^[A-Z]+-[0-9]+$ ]]; then + echo "A valid JIRA ID was found in branch name: $jira_id" + set +e + ./examples/jira-transition-example/bin/jira-transition-checker-linux-amd64 "Done" $jira_id > predicate.json + # add --failOnMissingTransition to fail the build if the JIRA does not pass the transition check + EXIT_CODE=$? + set -e + # create evidence only if the jira transition checker was successful + if [ $EXIT_CODE -eq 0 ]; then + # Attach evidence onto build using JFrog CLI + jf evd create \ + --build-name $GITHUB_WORKFLOW \ + --build-number "${{ github.run_number }}" \ + --predicate ./predicate.json \ + --predicate-type https://jfrog.com/evidence/build-jira-transition/v1 \ + --key "${{ secrets.JIRA_TEST_PKEY }}" \ + --key-alias ${{ vars.JIRA_TEST_KEY }} + else + echo "JIRA transition checked completed with an error, or not all JIRAs pass the transition checked" + fi + else + echo "No valid JIRA ID located in branch name: $BRANCH_NAME" + fi diff --git a/.github/workflows/sonar-evidence-example.yml b/.github/workflows/sonar-evidence-example.yml new file mode 100644 index 0000000..41ffdd3 --- /dev/null +++ b/.github/workflows/sonar-evidence-example.yml @@ -0,0 +1,121 @@ +name: sonar-evidence-example + +on: + workflow_dispatch: # This allows manual triggering of the workflow + push: + branches: + - CCS-2-Additional_evidence_examples + pull_request: + branches: + - CCS-2-Additional_evidence_examples +permissions: + id-token: write + contents: read + +jobs: + docker-build-with-sonar-evidence: + runs-on: ubuntu-latest + env: + DOCKER_REPO: 'test-docker-local' + IMAGE_NAME: 'my-very-cool-image:${{ github.run_number }}' + steps: + - name: Display workflow and job names + run: | + echo "Workflow name: $GITHUB_WORKFLOW" + echo "Job name: $GITHUB_JOB" + + - name: Install jfrog cli + id: setup-cli + uses: jfrog/setup-jfrog-cli@v4 + env: + JF_URL: ${{ vars.ARTIFACTORY_URL }} + with: + oidc-provider-name: jfrog-github-oidc + + - uses: actions/checkout@v4 + + - name: Install SonarQube Scanner + run: | + curl -sL -sSLo sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-6.2.1.4610.zip + unzip sonar-scanner.zip + export PATH=$PATH:$PWD/sonar-scanner-6.2.1.4610/bin + pwd + ls -l $PWD/sonar-scanner-6.2.1.4610/bin/ + echo "$PWD/sonar-scanner-6.2.1.4610/bin" + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '21' # Specify the desired Java version here + distribution: 'temurin' # You can also use 'temurin', 'zulu', etc. + + - name: Run SonarScanner + id: run-sonar-scanner + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + $PWD/sonar-scanner-6.2.1.4610/bin/sonar-scanner \ + -Dsonar.projectKey=test-evidence \ + -Dsonar.organization=my-evidence-test-org \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.java.jdkHome=$JAVA_HOME \ + -Dsonar.verbose=true \ + -Dsonar.token=$SONAR_TOKEN + # create evidence from sonar-scan analysis + set +e + # --FailOnAnalysisFailure causes a failure on gateway-failed sonar analysis + ./examples/sonar-scan-example/bin/sonar-scan-extractor-linux-amd64 --reportTaskFile=$PWD/.scannerwork/report-task.txt > predicate.json + EXIT_CODE=$? + set -e + # write the exit code to the github output so that it can be used in the evidence creation step + echo "------predicate.json------" + cat predicate.json + echo "------sonar-scan.log------" + cat sonar-scan.log + echo "------EXIT------" + echo "create-sonar-evidence=$EXIT_CODE" + echo "create-sonar-evidence=$EXIT_CODE" >> $GITHUB_OUTPUT + + + - name: Log in to Artifactory Docker Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.ARTIFACTORY_URL }} + username: ${{ steps.setup-cli.outputs.oidc-user }} + password: ${{ steps.setup-cli.outputs.oidc-token }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and Push Docker image + uses: docker/build-push-action@v6 + id: docker-build + with: + push: true + provenance: false + platforms: linux/amd64 #, linux/arm64 + build-args: REPO_URL=${{ vars.JF_URL }}/example-project-docker-dev-remote + tags: ${{ vars.JF_URL }}/${{ env.DOCKER_REPO }}/${{ env.IMAGE_NAME }} + + - name: add docker package to build + run: | + echo "${{ vars.JF_URL }}/${{ env.DOCKER_REPO }}/${{ env.IMAGE_NAME }}@${{ steps.docker-build.outputs.digest }}" > metadata.json + jf rt build-docker-create ${{ env.DOCKER_REPO }} --image-file metadata.json --build-name $GITHUB_WORKFLOW --build-number ${{ github.run_number }} + + - name: Publish build info + if: ${{ true }} + run: | + jf rt build-collect-env + jf rt build-add-git + jf rt build-publish + + - name: Create evidence + if: ${{ steps.run-sonar-scanner.outputs.create-sonar-evidence == 0 }} + run: | + # Attach evidence onto build using JFrog CLI + jf evd create \ + --build-name $GITHUB_WORKFLOW \ + --build-number "${{ github.run_number }}" \ + --predicate ./predicate.json \ + --predicate-type https://jfrog.com/evidence/sonar-scan/v1 \ + --key "${{ secrets.JIRA_TEST_PKEY }}" \ + --key-alias ${{ vars.JIRA_TEST_KEY }} \ No newline at end of file diff --git a/.github/workflows/zap-evidence-example.yml b/.github/workflows/zap-evidence-example.yml new file mode 100644 index 0000000..ed7f631 --- /dev/null +++ b/.github/workflows/zap-evidence-example.yml @@ -0,0 +1,59 @@ +name: zap-evidence-example + +on: + workflow_dispatch: # This allows manual triggering of the workflow + push: + branches: + - CCS-2-Additional_evidence_examples + pull_request: + branches: + - CCS-2-Additional_evidence_examples +permissions: + id-token: write + contents: read + + +jobs: + zap-evidence-example: + runs-on: ubuntu-latest + env: + DOCKER_REPO: 'test-docker-local' + IMAGE_NAME: 'my-very-cool-image' + IMAGE_TAG: '40' + BUILD_NAME: 'zap-evidence-example' + steps: + + - name: Install jfrog cli + id: setup-cli + uses: jfrog/setup-jfrog-cli@v4 + env: + JF_URL: ${{ vars.ARTIFACTORY_URL }} + with: + oidc-provider-name: jfrog-github-oidc + + - uses: actions/checkout@v4 + + - name: ZAP Scan + run: | + docker pull ghcr.io/zaproxy/zaproxy:stable + # zap test the mock site https://www.example.com + docker run -v /tmp:/zap/wrk/:rw -t ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py -t https://www.example.com -J report_json.json || true + echo "Zap completed!" + ls -ltr /tmp + # create summary json + cat /tmp/report_json.json | jq -r '.site[].alerts[].riskcode' | sort | uniq -c | awk '{print "{\"riskcode\":\"" $2 "\", \"count\":" $1 "},"}' | sed '$ s/,$//' | awk 'BEGIN {print "["} {print} END {print "]"}' > report_summary.json + # create full report predicate + jq -s '{summary: .[0], details: .[1]}' report_summary.json /tmp/report_json.json > summary.json + echo "-----------Summary of ZAP scan-----------" + cat summary.json + + - name: Evidence on docker + run: | + jf evd create \ + --package-name ${{ env.IMAGE_NAME }} \ + --package-version "${{ env.IMAGE_TAG }}" \ + --package-repo-name ${{ env.DOCKER_REPO }} \ + --key "${{ secrets.JIRA_TEST_PKEY }}" \ + --key-alias ${{ vars.JIRA_TEST_KEY }} \ + --predicate ./summary.json \ + --predicate-type https://jfrog.com/evidence/zap-scan/v1 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b85059b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/examples/sonar-scan-example/sonar-scanner-4.6.2.2472-linux/* +/examples/sonar-scan-example/bin/* +/examples/jira-transition-example/bin/* \ No newline at end of file diff --git a/README.md b/README.md index 6b741e1..7d62dc4 100644 --- a/README.md +++ b/README.md @@ -190,3 +190,4 @@ When the Evidence service is used in conjunction with JFrog Xray, each Release B To see a sample rego policy, go [here](https://github.com/jfrog/Evidence-Examples/blob/main/policy/policy.rego). For more information about integrating Release Lifecycle Management and Evidence with Xray, see [Scan Release Bundles (v2) with Xray](https://jfrog.com/help/r/jfrog-artifactory-documentation/scan-release-bundles-v2-with-xray). + diff --git a/examples/jira-transition-example/README.md b/examples/jira-transition-example/README.md new file mode 100644 index 0000000..3339a2c --- /dev/null +++ b/examples/jira-transition-example/README.md @@ -0,0 +1,28 @@ +# Create JIRA Transition Evidence from the build CI and attach it to the build info +JIRA is an important tool for tracking issues and managing projects and holds all requirements for software changes as Tasks. +For compliant software development, it is important to track requirements review and approval process as these confirm proper approval for code changes done and released. +To allow automation of proper requirements review and approval, we create an evidence of any JIRA linked to the code commits during the build with confirmation it went through approval status before code was committed. +Every company defines a different approval status, so in our example we allow the calling code send the name of the transition that shold be checked. + +pre-requisites: +1. Hold a cloud JIRA server (for selfhosted jira server, few code adjustments are required) +2. Allow network access from your CI server to Jira server +3. Define few environment variables: jira_url, jira_token, jira_username +4. Commit comments must include the JIRA issue ID (e.g. -1234) + +The example is based on the following steps: +1. get the relevant commit IDs +2. extract the JIRA IDs from all the build commits +3. call the jira-transition-checker utility (use the binary for your build platform) with these arguments: "transition name" JIRA-ID [,JIRA-ID] +for example: + ``./examples/jira-transition-example/bin/jira-transition-checker-linux-amd64 "Finance Approval" JIRA-486 PROJ-111 > predicate.json`` +optional arg: `--failOnMissingTransition` whihc will fail the script if any of the JIRA IDs sent did not pass the transition check +4. call the evidence create cli with the predicate.json file +for example: +``jf evd create \ + --build-name "${{ env.BUILD_NAME }}" \ + --build-number "${{ github.run_number }}" \ + --predicate ./predicate.json \ + --predicate-type https://jfrog.com/evidence/requirements-approval/v1 \ + --key "${{ secrets.JIRA_TEST_PKEY }}" \ + --key-alias ${{ vars.JIRA_TEST_KEY }}`` diff --git a/examples/jira-transition-example/build-binary.sh b/examples/jira-transition-example/build-binary.sh new file mode 100755 index 0000000..0784b28 --- /dev/null +++ b/examples/jira-transition-example/build-binary.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Script inspired by https://www.digitalocean.com/community/tutorials/how-to-build-go-executables-for-multiple-platforms-on-ubuntu-16-04 + +errorExit () { + echo; echo "ERROR: $1"; echo + exit 1 +} + +BIN=jira-transition-checker +rm -rf bin +mkdir -p bin + +echo "Building $BIN" +#platforms=("darwin/amd64" "linux/arm64" "linux/amd64" "windows/amd64" "windows/386") +platforms=("linux/arm64" "linux/amd64" "darwin/arm64" ) + +for p in "${platforms[@]}"; do + platform_array=(${p//\// }) + GOOS=${platform_array[0]} + GOARCH=${platform_array[1]} + + echo -e "\nBuilding" + echo "OS: $GOOS" + echo "ARCH: $GOARCH" + final_name=$BIN'-'$GOOS'-'$GOARCH + if [ "$GOOS" = "windows" ]; then + final_name+='.exe' + fi + + env GOOS="$GOOS" GOARCH="$GOARCH" go build -o bin/$final_name . || errorExit "Building $final_name failed" +done + +echo -e "\nDone!\nThe following binaries were created in the bin/ directory:" +ls -1 bin/ +echo \ No newline at end of file diff --git a/examples/jira-transition-example/main.go b/examples/jira-transition-example/main.go new file mode 100644 index 0000000..23b7600 --- /dev/null +++ b/examples/jira-transition-example/main.go @@ -0,0 +1,155 @@ +package main +import ( + "context" + "encoding/json" + "os" + "fmt" + jira "github.com/andygrunwald/go-jira/v2/cloud" + ) + /* + JiraTransitionResponse is the json formatted predicate that will be returned to the calling build process for cresting an evidence + its structure should be: + + { + "transition": "", + "allJiraTransitionsFound": "", + "tasks": [ + { + "jira_id": "", + "summary": "", + "transition_found": "" + "author": "", + "author_user_name": "", + "transition_time": "2025-02-04T08:14:03.559+0200" + } + ] + } + + notice that the calling client should first check that return value was 0 before using the response JSON, + otherwise the response is an error message which cannot be parsed + */ + +type TransitionCheckResponse struct { + Transition string `json:"transition"` + AllJiraTransitionsFound bool `json:"allJiraTransitionsFound"` + Tasks []JiraTransitionResult `json:"tasks"` +} + +type JiraTransitionResult struct { + JiraId string `json:"jira_id"` + Summary string `json:"summary"` + TransitionFound bool `json:"transition_found"` + Author string `json:"author"` + AuthorEmail string `json:"author_user_name"` + TransitionTime string `json:"transition_time"` +} + +func main() { + // get checked transition name and JIRA IDs from command-line arguments + if len(os.Args) < 3 { + fmt.Println("Insufficient command-line arguments provided, please send checked transition name and at least one JIRA ID(s)") + return + } + transitionArgPosition := 1 + jiraArgPosition := 2 + failOnMissingTransition := false + for i, arg := range os.Args { + if i == 0 { + continue + } + if strings.HasPrefix(arg, "--failOnMissingTransition") { + failOnMissingTransition = true + // removing the argument from the list + os.Args = append(os.Args[:indexToRemove], os.Args[indexToRemove+1:]...) + } + } + + + transitionChecked := os.Args[transitionArgPosition] + // Create a new Jira client + jira_token := os.Getenv("jira_token") + if jira_token == "" { + fmt.Println("JIRA token not found, set jira_token variable") + os.Exit(1) + } + jira_url := os.Getenv("jira_url") + if jira_url == "" { + fmt.Println("JIRA URL not found, set jira_url variable") + os.Exit(1) + } + jira_username := os.Getenv("jira_username") + if jira_username == "" { + fmt.Println("JIRA username not found, set jira_username variable") + os.Exit(1) + } + // connect to JIRA + tp := jira.BasicAuthTransport{ + Username: jira_username, + APIToken: jira_token, + } + client, err := jira.NewClient(jira_url, tp.Client()) + if err != nil { + fmt.Println("jira.NewClient error: %v\n", err) + os.Exit(1) + } + // initialize the response + transitionCheckResponse := TransitionCheckResponse{} + transitionCheckResponse.AllJiraTransitionsFound = true + transitionCheckResponse.Transition = transitionChecked + transitionFound := false + // loop over all JIRAs sent to the fucntion + for _, jiraId := range os.Args[jiraArgPosition:] { + //fmt.Println("-----------Checking JIRA ", jiraId) + transitionFound = false + issue, _, _ := client.Issue.Get(context.Background(), jiraId , &jira.GetQueryOptions{Expand: "changelog"}) + if issue == nil { + fmt.Println("Got error for extracting issue with jira id: ", jiraId, "error", err) + os.Exit(1) + } + // adding the jira result to the list of results + jiraTransitionResult := JiraTransitionResult{ + JiraId: jiraId, + Summary: issue.Fields.Summary, + } + + if len(issue.Changelog.Histories) > 0 { + //fmt.Println("history found for jira id:", jiraId) + for _, history := range issue.Changelog.Histories { + for _, changelogItems := range history.Items { + //fmt.Println("jira id:", jiraId, "field", changelogItems.Field, "FieldType", changelogItems.FieldType, "toString", changelogItems.ToString) + if changelogItems.Field == "status" { + //fmt.Println("Transition for jira", jiraId, "FromString", changelogItems.FromString, "ToString" , changelogItems.ToString, "Created", history.Created, "Author", history.Author) + if changelogItems.ToString == transitionChecked { + transitionFound = true + jiraTransitionResult.Author = history.Author.DisplayName + jiraTransitionResult.AuthorEmail = history.Author.EmailAddress + jiraTransitionResult.TransitionTime = history.Created + // fmt.Println("Transition name for jira", jiraId, "found") + break // once found we can continue to the next jira + } + } + } + } + } + jiraTransitionResult.TransitionFound = transitionFound + transitionCheckResponse.Tasks = append(transitionCheckResponse.Tasks, jiraTransitionResult) + // check if all transitions are found + if !transitionFound { + transitionCheckResponse.AllJiraTransitionsFound = false + } + } + if failOnMissingTransition && !transitionCheckResponse.AllJiraTransitionsFound { + fmt.Println("Not all JIRA transitions found") + os.Exit(1) + } + // marshal the response to JSON + jsonBytes, err := json.Marshal(transitionCheckResponse) + if err != nil { + fmt.Println("Error marshaling JSON", err) + os.Exit(1) + } + //logger.Println("returning response", response) + + // return response to caller through stdout + os.Stdout.Write(jsonBytes) + } diff --git a/examples/service-now evidence/CryptoJS b/examples/service-now evidence/CryptoJS new file mode 100644 index 0000000..21c201c --- /dev/null +++ b/examples/service-now evidence/CryptoJS @@ -0,0 +1,101 @@ +/* + * jsrsasign(rsa) 11.1.0 (2024-02-01) (c) 2010-2023 Kenji Urushima | kjur.github.io/jsrsasign/license + */ +var VERSION = "11.1.0"; +var VERSION_FULL = "jsrsasign(rsa) 11.1.0 (2024-02-01) (c) 2010-2023 Kenji Urushima | kjur.github.io/jsrsasign/license"; +var navigator = {'userAgent':'', 'appName':''}; +var window; +/*! CryptoJS v3.1.2 core-fix.js + * code.google.com/p/crypto-js + * (c) 2009-2013 by Jeff Mott. All rights reserved. + * code.google.com/p/crypto-js/wiki/License + * THIS IS FIX of 'core.js' to fix Hmac issue. + * https://code.google.com/p/crypto-js/issues/detail?id=84 + * https://crypto-js.googlecode.com/svn-history/r667/branches/3.x/src/core.js + */ +var CryptoJS=CryptoJS||(function(e,g){var a={};var b=a.lib={};var j=b.Base=(function(){function n(){}return{extend:function(p){n.prototype=this;var o=new n();if(p){o.mixIn(p)}if(!o.hasOwnProperty("init")){o.init=function(){o.$super.init.apply(this,arguments)}}o.init.prototype=o;o.$super=this;return o},create:function(){var o=this.extend();o.init.apply(o,arguments);return o},init:function(){},mixIn:function(p){for(var o in p){if(p.hasOwnProperty(o)){this[o]=p[o]}}if(p.hasOwnProperty("toString")){this.toString=p.toString}},clone:function(){return this.init.prototype.extend(this)}}}());var l=b.WordArray=j.extend({init:function(o,n){o=this.words=o||[];if(n!=g){this.sigBytes=n}else{this.sigBytes=o.length*4}},toString:function(n){return(n||h).stringify(this)},concat:function(t){var q=this.words;var p=t.words;var n=this.sigBytes;var s=t.sigBytes;this.clamp();if(n%4){for(var r=0;r>>2]>>>(24-(r%4)*8))&255;q[(n+r)>>>2]|=o<<(24-((n+r)%4)*8)}}else{for(var r=0;r>>2]=p[r>>>2]}}this.sigBytes+=s;return this},clamp:function(){var o=this.words;var n=this.sigBytes;o[n>>>2]&=4294967295<<(32-(n%4)*8);o.length=e.ceil(n/4)},clone:function(){var n=j.clone.call(this);n.words=this.words.slice(0);return n},random:function(p){var o=[];for(var n=0;n>>2]>>>(24-(n%4)*8))&255;q.push((s>>>4).toString(16));q.push((s&15).toString(16))}return q.join("")},parse:function(p){var n=p.length;var q=[];for(var o=0;o>>3]|=parseInt(p.substr(o,2),16)<<(24-(o%8)*4)}return new l.init(q,n/2)}};var d=m.Latin1={stringify:function(q){var r=q.words;var p=q.sigBytes;var n=[];for(var o=0;o>>2]>>>(24-(o%4)*8))&255;n.push(String.fromCharCode(s))}return n.join("")},parse:function(p){var n=p.length;var q=[];for(var o=0;o>>2]|=(p.charCodeAt(o)&255)<<(24-(o%4)*8)}return new l.init(q,n)}};var c=m.Utf8={stringify:function(n){try{return decodeURIComponent(escape(d.stringify(n)))}catch(o){throw new Error("Malformed UTF-8 data")}},parse:function(n){return d.parse(unescape(encodeURIComponent(n)))}};var i=b.BufferedBlockAlgorithm=j.extend({reset:function(){this._data=new l.init();this._nDataBytes=0},_append:function(n){if(typeof n=="string"){n=c.parse(n)}this._data.concat(n);this._nDataBytes+=n.sigBytes},_process:function(w){var q=this._data;var x=q.words;var n=q.sigBytes;var t=this.blockSize;var v=t*4;var u=n/v;if(w){u=e.ceil(u)}else{u=e.max((u|0)-this._minBufferSize,0)}var s=u*t;var r=e.min(s*4,n);if(s){for(var p=0;p>>2]&255}};f.BlockCipher=n.extend({cfg:n.cfg.extend({mode:m,padding:h}),reset:function(){n.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1; +this._mode=c.call(a,this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var p=f.CipherParams=k.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),m=(g.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt; +return(a?l.create([1398893684,1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=l.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return p.create({ciphertext:a,salt:c})}},j=f.SerializableCipher=k.extend({cfg:k.extend({format:m}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var e=a.createEncryptor(c,d);b=e.finalize(b);e=e.cfg;return p.create({ciphertext:b,key:c,iv:e.iv,algorithm:a,mode:e.mode,padding:e.padding, +blockSize:a.blockSize,formatter:d.format})},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),g=(g.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=l.random(8));a=v.create({keySize:b+c}).compute(a,d);c=l.create(a.words.slice(b),4*c);a.sigBytes=4*b;return p.create({key:a,iv:c,salt:d})}},s=f.PasswordBasedCipher=j.extend({cfg:j.cfg.extend({kdf:g}),encrypt:function(a, +b,c,d){d=this.cfg.extend(d);c=d.kdf.execute(c,a.keySize,a.ivSize);d.iv=c.iv;a=j.encrypt.call(this,a,b,c.key,d);a.mixIn(c);return a},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);c=d.kdf.execute(c,a.keySize,a.ivSize,b.salt);d.iv=c.iv;return j.decrypt.call(this,a,b,c.key,d)}})}(); + +/* +CryptoJS v3.1.2 sha1-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var k=CryptoJS,b=k.lib,m=b.WordArray,l=b.Hasher,d=[],b=k.algo.SHA1=l.extend({_doReset:function(){this._hash=new m.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(n,p){for(var a=this._hash.words,e=a[0],f=a[1],h=a[2],j=a[3],b=a[4],c=0;80>c;c++){if(16>c)d[c]=n[p+c]|0;else{var g=d[c-3]^d[c-8]^d[c-14]^d[c-16];d[c]=g<<1|g>>>31}g=(e<<5|e>>>27)+b+d[c];g=20>c?g+((f&h|~f&j)+1518500249):40>c?g+((f^h^j)+1859775393):60>c?g+((f&h|f&j|h&j)-1894007588):g+((f^h^ +j)-899497514);b=j;j=h;h=f<<30|f>>>2;f=e;e=g}a[0]=a[0]+e|0;a[1]=a[1]+f|0;a[2]=a[2]+h|0;a[3]=a[3]+j|0;a[4]=a[4]+b|0},_doFinalize:function(){var b=this._data,d=b.words,a=8*this._nDataBytes,e=8*b.sigBytes;d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=Math.floor(a/4294967296);d[(e+64>>>9<<4)+15]=a;b.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var b=l.clone.call(this);b._hash=this._hash.clone();return b}});k.SHA1=l._createHelper(b);k.HmacSHA1=l._createHmacHelper(b)})(); + +/* +CryptoJS v3.1.2 sha256-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(k){for(var g=CryptoJS,h=g.lib,v=h.WordArray,j=h.Hasher,h=g.algo,s=[],t=[],u=function(q){return 4294967296*(q-(q|0))|0},l=2,b=0;64>b;){var d;a:{d=l;for(var w=k.sqrt(d),r=2;r<=w;r++)if(!(d%r)){d=!1;break a}d=!0}d&&(8>b&&(s[b]=u(k.pow(l,0.5))),t[b]=u(k.pow(l,1/3)),b++);l++}var n=[],h=h.SHA256=j.extend({_doReset:function(){this._hash=new v.init(s.slice(0))},_doProcessBlock:function(q,h){for(var a=this._hash.words,c=a[0],d=a[1],b=a[2],k=a[3],f=a[4],g=a[5],j=a[6],l=a[7],e=0;64>e;e++){if(16>e)n[e]= +q[h+e]|0;else{var m=n[e-15],p=n[e-2];n[e]=((m<<25|m>>>7)^(m<<14|m>>>18)^m>>>3)+n[e-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+n[e-16]}m=l+((f<<26|f>>>6)^(f<<21|f>>>11)^(f<<7|f>>>25))+(f&g^~f&j)+t[e]+n[e];p=((c<<30|c>>>2)^(c<<19|c>>>13)^(c<<10|c>>>22))+(c&d^c&b^d&b);l=j;j=g;g=f;f=k+m|0;k=b;b=d;d=c;c=m+p|0}a[0]=a[0]+c|0;a[1]=a[1]+d|0;a[2]=a[2]+b|0;a[3]=a[3]+k|0;a[4]=a[4]+f|0;a[5]=a[5]+g|0;a[6]=a[6]+j|0;a[7]=a[7]+l|0},_doFinalize:function(){var d=this._data,b=d.words,a=8*this._nDataBytes,c=8*d.sigBytes; +b[c>>>5]|=128<<24-c%32;b[(c+64>>>9<<4)+14]=k.floor(a/4294967296);b[(c+64>>>9<<4)+15]=a;d.sigBytes=4*b.length;this._process();return this._hash},clone:function(){var b=j.clone.call(this);b._hash=this._hash.clone();return b}});g.SHA256=j._createHelper(h);g.HmacSHA256=j._createHmacHelper(h)})(Math); + +/* +CryptoJS v3.1.2 sha512-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){function a(){return d.create.apply(d,arguments)}for(var n=CryptoJS,r=n.lib.Hasher,e=n.x64,d=e.Word,T=e.WordArray,e=n.algo,ea=[a(1116352408,3609767458),a(1899447441,602891725),a(3049323471,3964484399),a(3921009573,2173295548),a(961987163,4081628472),a(1508970993,3053834265),a(2453635748,2937671579),a(2870763221,3664609560),a(3624381080,2734883394),a(310598401,1164996542),a(607225278,1323610764),a(1426881987,3590304994),a(1925078388,4068182383),a(2162078206,991336113),a(2614888103,633803317), +a(3248222580,3479774868),a(3835390401,2666613458),a(4022224774,944711139),a(264347078,2341262773),a(604807628,2007800933),a(770255983,1495990901),a(1249150122,1856431235),a(1555081692,3175218132),a(1996064986,2198950837),a(2554220882,3999719339),a(2821834349,766784016),a(2952996808,2566594879),a(3210313671,3203337956),a(3336571891,1034457026),a(3584528711,2466948901),a(113926993,3758326383),a(338241895,168717936),a(666307205,1188179964),a(773529912,1546045734),a(1294757372,1522805485),a(1396182291, +2643833823),a(1695183700,2343527390),a(1986661051,1014477480),a(2177026350,1206759142),a(2456956037,344077627),a(2730485921,1290863460),a(2820302411,3158454273),a(3259730800,3505952657),a(3345764771,106217008),a(3516065817,3606008344),a(3600352804,1432725776),a(4094571909,1467031594),a(275423344,851169720),a(430227734,3100823752),a(506948616,1363258195),a(659060556,3750685593),a(883997877,3785050280),a(958139571,3318307427),a(1322822218,3812723403),a(1537002063,2003034995),a(1747873779,3602036899), +a(1955562222,1575990012),a(2024104815,1125592928),a(2227730452,2716904306),a(2361852424,442776044),a(2428436474,593698344),a(2756734187,3733110249),a(3204031479,2999351573),a(3329325298,3815920427),a(3391569614,3928383900),a(3515267271,566280711),a(3940187606,3454069534),a(4118630271,4000239992),a(116418474,1914138554),a(174292421,2731055270),a(289380356,3203993006),a(460393269,320620315),a(685471733,587496836),a(852142971,1086792851),a(1017036298,365543100),a(1126000580,2618297676),a(1288033470, +3409855158),a(1501505948,4234509866),a(1607167915,987167468),a(1816402316,1246189591)],v=[],w=0;80>w;w++)v[w]=a();e=e.SHA512=r.extend({_doReset:function(){this._hash=new T.init([new d.init(1779033703,4089235720),new d.init(3144134277,2227873595),new d.init(1013904242,4271175723),new d.init(2773480762,1595750129),new d.init(1359893119,2917565137),new d.init(2600822924,725511199),new d.init(528734635,4215389547),new d.init(1541459225,327033209)])},_doProcessBlock:function(a,d){for(var f=this._hash.words, +F=f[0],e=f[1],n=f[2],r=f[3],G=f[4],H=f[5],I=f[6],f=f[7],w=F.high,J=F.low,X=e.high,K=e.low,Y=n.high,L=n.low,Z=r.high,M=r.low,$=G.high,N=G.low,aa=H.high,O=H.low,ba=I.high,P=I.low,ca=f.high,Q=f.low,k=w,g=J,z=X,x=K,A=Y,y=L,U=Z,B=M,l=$,h=N,R=aa,C=O,S=ba,D=P,V=ca,E=Q,m=0;80>m;m++){var s=v[m];if(16>m)var j=s.high=a[d+2*m]|0,b=s.low=a[d+2*m+1]|0;else{var j=v[m-15],b=j.high,p=j.low,j=(b>>>1|p<<31)^(b>>>8|p<<24)^b>>>7,p=(p>>>1|b<<31)^(p>>>8|b<<24)^(p>>>7|b<<25),u=v[m-2],b=u.high,c=u.low,u=(b>>>19|c<<13)^(b<< +3|c>>>29)^b>>>6,c=(c>>>19|b<<13)^(c<<3|b>>>29)^(c>>>6|b<<26),b=v[m-7],W=b.high,t=v[m-16],q=t.high,t=t.low,b=p+b.low,j=j+W+(b>>>0

>>0?1:0),b=b+c,j=j+u+(b>>>0>>0?1:0),b=b+t,j=j+q+(b>>>0>>0?1:0);s.high=j;s.low=b}var W=l&R^~l&S,t=h&C^~h&D,s=k&z^k&A^z&A,T=g&x^g&y^x&y,p=(k>>>28|g<<4)^(k<<30|g>>>2)^(k<<25|g>>>7),u=(g>>>28|k<<4)^(g<<30|k>>>2)^(g<<25|k>>>7),c=ea[m],fa=c.high,da=c.low,c=E+((h>>>14|l<<18)^(h>>>18|l<<14)^(h<<23|l>>>9)),q=V+((l>>>14|h<<18)^(l>>>18|h<<14)^(l<<23|h>>>9))+(c>>>0>>0?1: +0),c=c+t,q=q+W+(c>>>0>>0?1:0),c=c+da,q=q+fa+(c>>>0>>0?1:0),c=c+b,q=q+j+(c>>>0>>0?1:0),b=u+T,s=p+s+(b>>>0>>0?1:0),V=S,E=D,S=R,D=C,R=l,C=h,h=B+c|0,l=U+q+(h>>>0>>0?1:0)|0,U=A,B=y,A=z,y=x,z=k,x=g,g=c+b|0,k=q+s+(g>>>0>>0?1:0)|0}J=F.low=J+g;F.high=w+k+(J>>>0>>0?1:0);K=e.low=K+x;e.high=X+z+(K>>>0>>0?1:0);L=n.low=L+y;n.high=Y+A+(L>>>0>>0?1:0);M=r.low=M+B;r.high=Z+U+(M>>>0>>0?1:0);N=G.low=N+h;G.high=$+l+(N>>>0>>0?1:0);O=H.low=O+C;H.high=aa+R+(O>>>0>>0?1:0);P=I.low=P+D; +I.high=ba+S+(P>>>0>>0?1:0);Q=f.low=Q+E;f.high=ca+V+(Q>>>0>>0?1:0)},_doFinalize:function(){var a=this._data,d=a.words,f=8*this._nDataBytes,e=8*a.sigBytes;d[e>>>5]|=128<<24-e%32;d[(e+128>>>10<<5)+30]=Math.floor(f/4294967296);d[(e+128>>>10<<5)+31]=f;a.sigBytes=4*d.length;this._process();return this._hash.toX32()},clone:function(){var a=r.clone.call(this);a._hash=this._hash.clone();return a},blockSize:32});n.SHA512=r._createHelper(e);n.HmacSHA512=r._createHmacHelper(e)})(); + +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64pad="=";function hex2b64(d){var b;var e;var a="";for(b=0;b+3<=d.length;b+=3){e=parseInt(d.substring(b,b+3),16);a+=b64map.charAt(e>>6)+b64map.charAt(e&63)}if(b+1==d.length){e=parseInt(d.substring(b,b+1),16);a+=b64map.charAt(e<<2)}else{if(b+2==d.length){e=parseInt(d.substring(b,b+2),16);a+=b64map.charAt(e>>2)+b64map.charAt((e&3)<<4)}}if(b64pad){while((a.length&3)>0){a+=b64pad}}return a}function b64tohex(f){var d="";var e;var b=0;var c;var a;for(e=0;e>2);c=a&3;b=1}else{if(b==1){d+=int2char((c<<2)|(a>>4));c=a&15;b=2}else{if(b==2){d+=int2char(c);d+=int2char(a>>2);c=a&3;b=3}else{d+=int2char((c<<2)|(a>>4));d+=int2char(a&15);b=0}}}}if(b==1){d+=int2char(c<<2)}return d}function b64toBA(e){var d=b64tohex(e);var c;var b=new Array();for(c=0;2*c=0){var d=a*this[f++]+b[e]+h;h=Math.floor(d/67108864);b[e++]=d&67108863}return h}function am2(f,q,r,e,o,a){var k=q&32767,p=q>>15;while(--a>=0){var d=this[f]&32767;var g=this[f++]>>15;var b=p*d+g*k;d=k*d+((b&32767)<<15)+r[e]+(o&1073741823);o=(d>>>30)+(b>>>15)+p*g+(o>>>30);r[e++]=d&1073741823}return o}function am3(f,q,r,e,o,a){var k=q&16383,p=q>>14;while(--a>=0){var d=this[f]&16383;var g=this[f++]>>14;var b=p*d+g*k;d=k*d+((b&16383)<<14)+r[e]+o;o=(d>>28)+(b>>14)+p*g;r[e++]=d&268435455}return o}if(j_lm&&(navigator.appName=="Microsoft Internet Explorer")){BigInteger.prototype.am=am2;dbits=30}else{if(j_lm&&(navigator.appName!="Netscape")){BigInteger.prototype.am=am1;dbits=26}else{BigInteger.prototype.am=am3;dbits=28}}BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<=0;--a){b[a]=this[a]}b.t=this.t;b.s=this.s}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0){this[0]=a}else{if(a<-1){this[0]=a+this.DV}else{this.t=0}}}function nbv(a){var b=nbi();b.fromInt(a);return b}function bnpFromString(h,c){var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==256){e=8}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{this.fromRadix(h,c);return}}}}}}this.t=0;this.s=0;var g=h.length,d=false,f=0;while(--g>=0){var a=(e==8)?h[g]&255:intAt(h,g);if(a<0){if(h.charAt(g)=="-"){d=true}continue}d=false;if(f==0){this[this.t++]=a}else{if(f+e>this.DB){this[this.t-1]|=(a&((1<<(this.DB-f))-1))<>(this.DB-f))}else{this[this.t-1]|=a<=this.DB){f-=this.DB}}if(e==8&&(h[0]&128)!=0){this.s=-1;if(f>0){this[this.t-1]|=((1<<(this.DB-f))-1)<0&&this[this.t-1]==a){--this.t}}function bnToString(c){if(this.s<0){return"-"+this.negate().toString(c)}var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{return this.toRadix(c)}}}}}var g=(1<0){if(j>j)>0){a=true;h=int2char(l)}while(f>=0){if(j>(j+=this.DB-e)}else{l=(this[f]>>(j-=e))&g;if(j<=0){j+=this.DB;--f}}if(l>0){a=true}if(a){h+=int2char(l)}}}return a?h:"0"}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return(this.s<0)?this.negate():this}function bnCompareTo(b){var d=this.s-b.s;if(d!=0){return d}var c=this.t;d=c-b.t;if(d!=0){return(this.s<0)?-d:d}while(--c>=0){if((d=this[c]-b[c])!=0){return d}}return 0}function nbits(a){var c=1,b;if((b=a>>>16)!=0){a=b;c+=16}if((b=a>>8)!=0){a=b;c+=8}if((b=a>>4)!=0){a=b;c+=4}if((b=a>>2)!=0){a=b;c+=2}if((b=a>>1)!=0){a=b;c+=1}return c}function bnBitLength(){if(this.t<=0){return 0}return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM))}function bnpDLShiftTo(c,b){var a;for(a=this.t-1;a>=0;--a){b[a+c]=this[a]}for(a=c-1;a>=0;--a){b[a]=0}b.t=this.t+c;b.s=this.s}function bnpDRShiftTo(c,b){for(var a=c;a=0;--d){e[d+f+1]=(this[d]>>a)|h;h=(this[d]&g)<=0;--d){e[d]=0}e[f]=h;e.t=this.t+f+1;e.s=this.s;e.clamp()}function bnpRShiftTo(g,d){d.s=this.s;var e=Math.floor(g/this.DB);if(e>=this.t){d.t=0;return}var b=g%this.DB;var a=this.DB-b;var f=(1<>b;for(var c=e+1;c>b}if(b>0){d[this.t-e-1]|=(this.s&f)<>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g-=d.s}f.s=(g<0)?-1:0;if(g<-1){f[e++]=this.DV+g}else{if(g>0){f[e++]=g}}f.t=e;f.clamp()}function bnpMultiplyTo(c,e){var b=this.abs(),f=c.abs();var d=b.t;e.t=d+f.t;while(--d>=0){e[d]=0}for(d=0;d=0){d[b]=0}for(b=0;b=a.DV){d[b+a.t]-=a.DV;d[b+a.t+1]=1}}if(d.t>0){d[d.t-1]+=a.am(b,a[b],d,2*b,0,1)}d.s=0;d.clamp()}function bnpDivRemTo(n,h,g){var w=n.abs();if(w.t<=0){return}var k=this.abs();if(k.t0){w.lShiftTo(v,d);k.lShiftTo(v,g)}else{w.copyTo(d);k.copyTo(g)}var p=d.t;var b=d[p-1];if(b==0){return}var o=b*(1<1)?d[p-2]>>this.F2:0);var A=this.FV/o,z=(1<=0){g[g.t++]=1;g.subTo(f,g)}BigInteger.ONE.dlShiftTo(p,f);f.subTo(d,d);while(d.t=0){var c=(g[--u]==b)?this.DM:Math.floor(g[u]*A+(g[u-1]+x)*z);if((g[u]+=d.am(0,c,g,s,0,p))0){g.rShiftTo(v,g)}if(a<0){BigInteger.ZERO.subTo(g,g)}}function bnMod(b){var c=nbi();this.abs().divRemTo(b,null,c);if(this.s<0&&c.compareTo(BigInteger.ZERO)>0){b.subTo(c,c)}return c}function Classic(a){this.m=a}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0){return a.mod(this.m)}else{return a}}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1){return 0}var a=this[0];if((a&1)==0){return 0}var b=a&3;b=(b*(2-(a&15)*b))&15;b=(b*(2-(a&255)*b))&255;b=(b*(2-(((a&65535)*b)&65535)))&65535;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0){this.m.subTo(b,b)}return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}function montReduce(a){while(a.t<=this.mt2){a[a.t++]=0}for(var c=0;c>15)*this.mpl)&this.um)<<15))&a.DM;b=c+this.m.t;a[b]+=this.m.am(0,d,a,c,0,this.m.t);while(a[b]>=a.DV){a[b]-=a.DV;a[++b]++}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0}function bnpExp(h,j){if(h>4294967295||h<1){return BigInteger.ONE}var f=nbi(),a=nbi(),d=j.convert(this),c=nbits(h)-1;d.copyTo(f);while(--c>=0){j.sqrTo(f,a);if((h&(1<0){j.mulTo(a,d,f)}else{var b=f;f=a;a=b}}return j.revert(f)}function bnModPowInt(b,a){var c;if(b<256||a.isEven()){c=new Classic(a)}else{c=new Montgomery(a)}return this.exp(b,c)}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1); +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function bnClone(){var a=nbi();this.copyTo(a);return a}function bnIntValue(){if(this.s<0){if(this.t==1){return this[0]-this.DV}else{if(this.t==0){return -1}}}else{if(this.t==1){return this[0]}else{if(this.t==0){return 0}}}return((this[1]&((1<<(32-this.DB))-1))<>24}function bnShortValue(){return(this.t==0)?this.s:(this[0]<<16)>>16}function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}function bnSigNum(){if(this.s<0){return -1}else{if(this.t<=0||(this.t==1&&this[0]<=0)){return 0}else{return 1}}}function bnpToRadix(c){if(c==null){c=10}if(this.signum()==0||c<2||c>36){return"0"}var f=this.chunkSize(c);var e=Math.pow(c,f);var i=nbv(e),j=nbi(),h=nbi(),g="";this.divRemTo(i,j,h);while(j.signum()>0){g=(e+h.intValue()).toString(c).substr(1)+g;j.divRemTo(i,j,h)}return h.intValue().toString(c)+g}function bnpFromRadix(m,h){this.fromInt(0);if(h==null){h=10}var f=this.chunkSize(h);var g=Math.pow(h,f),e=false,a=0,l=0;for(var c=0;c=f){this.dMultiply(g);this.dAddOffset(l,0);a=0;l=0}}if(a>0){this.dMultiply(Math.pow(h,a));this.dAddOffset(l,0)}if(e){BigInteger.ZERO.subTo(this,this)}}function bnpFromNumber(f,e,h){if("number"==typeof e){if(f<2){this.fromInt(1)}else{this.fromNumber(f,h);if(!this.testBit(f-1)){this.bitwiseTo(BigInteger.ONE.shiftLeft(f-1),op_or,this)}if(this.isEven()){this.dAddOffset(1,0)}while(!this.isProbablePrime(e)){this.dAddOffset(2,0);if(this.bitLength()>f){this.subTo(BigInteger.ONE.shiftLeft(f-1),this)}}}}else{var d=new Array(),g=f&7;d.length=(f>>3)+1;e.nextBytes(d);if(g>0){d[0]&=((1<0){if(e>e)!=(this.s&this.DM)>>e){c[a++]=f|(this.s<<(this.DB-e))}while(b>=0){if(e<8){f=(this[b]&((1<>(e+=this.DB-8)}else{f=(this[b]>>(e-=8))&255;if(e<=0){e+=this.DB;--b}}if((f&128)!=0){f|=-256}if(a==0&&(this.s&128)!=(f&128)){++a}if(a>0||f!=this.s){c[a++]=f}}}return c}function bnEquals(b){return(this.compareTo(b)==0)}function bnMin(b){return(this.compareTo(b)<0)?this:b}function bnMax(b){return(this.compareTo(b)>0)?this:b}function bnpBitwiseTo(c,h,e){var d,g,b=Math.min(c.t,this.t);for(d=0;d>=16;b+=16}if((a&255)==0){a>>=8;b+=8}if((a&15)==0){a>>=4;b+=4}if((a&3)==0){a>>=2;b+=2}if((a&1)==0){++b}return b}function bnGetLowestSetBit(){for(var a=0;a=this.t){return(this.s!=0)}return((this[a]&(1<<(b%this.DB)))!=0)}function bnpChangeBit(c,b){var a=BigInteger.ONE.shiftLeft(c);this.bitwiseTo(a,b,a);return a}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}function bnpAddTo(d,f){var e=0,g=0,b=Math.min(d.t,this.t);while(e>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g+=d.s}f.s=(g<0)?-1:0;if(g>0){f[e++]=g}else{if(g<-1){f[e++]=this.DV+g}}f.t=e;f.clamp()}function bnAdd(b){var c=nbi();this.addTo(b,c);return c}function bnSubtract(b){var c=nbi();this.subTo(b,c);return c}function bnMultiply(b){var c=nbi();this.multiplyTo(b,c);return c}function bnSquare(){var a=nbi();this.squareTo(a);return a}function bnDivide(b){var c=nbi();this.divRemTo(b,c,null);return c}function bnRemainder(b){var c=nbi();this.divRemTo(b,null,c);return c}function bnDivideAndRemainder(b){var d=nbi(),c=nbi();this.divRemTo(b,d,c);return new Array(d,c)}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}function bnpDAddOffset(b,a){if(b==0){return}while(this.t<=a){this[this.t++]=0}this[a]+=b;while(this[a]>=this.DV){this[a]-=this.DV;if(++a>=this.t){this[this.t++]=0}++this[a]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,c,b){a.multiplyTo(c,b)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp())}function bnpMultiplyLowerTo(b,f,e){var d=Math.min(this.t+b.t,f);e.s=0;e.t=d;while(d>0){e[--d]=0}var c;for(c=e.t-this.t;d=0){d[c]=0}for(c=Math.max(e-this.t,0);c2*this.m.t){return a.mod(this.m)}else{if(a.compareTo(this.m)<0){return a}else{var b=nbi();a.copyTo(b);this.reduce(b);return b}}}function barrettRevert(a){return a}function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);if(a.t>this.m.t+1){a.t=this.m.t+1;a.clamp()}this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(a.compareTo(this.r2)<0){a.dAddOffset(1,this.m.t+1)}a.subTo(this.r2,a);while(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Barrett.prototype.convert=barrettConvert;Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;function bnModPow(q,f){var o=q.bitLength(),h,b=nbv(1),v;if(o<=0){return b}else{if(o<18){h=1}else{if(o<48){h=3}else{if(o<144){h=4}else{if(o<768){h=5}else{h=6}}}}}if(o<8){v=new Classic(f)}else{if(f.isEven()){v=new Barrett(f)}else{v=new Montgomery(f)}}var p=new Array(),d=3,s=h-1,a=(1<1){var A=nbi();v.sqrTo(p[1],A);while(d<=a){p[d]=nbi();v.mulTo(A,p[d-2],p[d]);d+=2}}var l=q.t-1,x,u=true,c=nbi(),y;o=nbits(q[l])-1;while(l>=0){if(o>=s){x=(q[l]>>(o-s))&a}else{x=(q[l]&((1<<(o+1))-1))<<(s-o);if(l>0){x|=q[l-1]>>(this.DB+o-s)}}d=h;while((x&1)==0){x>>=1;--d}if((o-=d)<0){o+=this.DB;--l}if(u){p[x].copyTo(b);u=false}else{while(d>1){v.sqrTo(b,c);v.sqrTo(c,b);d-=2}if(d>0){v.sqrTo(b,c)}else{y=b;b=c;c=y}v.mulTo(c,p[x],b)}while(l>=0&&(q[l]&(1<0){b.rShiftTo(f,b);h.rShiftTo(f,h)}while(b.signum()>0){if((d=b.getLowestSetBit())>0){b.rShiftTo(d,b)}if((d=h.getLowestSetBit())>0){h.rShiftTo(d,h)}if(b.compareTo(h)>=0){b.subTo(h,b);b.rShiftTo(1,b)}else{h.subTo(b,h);h.rShiftTo(1,h)}}if(f>0){h.lShiftTo(f,h)}return h}function bnpModInt(e){if(e<=0){return 0}var c=this.DV%e,b=(this.s<0)?e-1:0;if(this.t>0){if(c==0){b=this[0]%e}else{for(var a=this.t-1;a>=0;--a){b=(c*b+this[a])%e}}}return b}function bnModInverse(f){var j=f.isEven();if((this.isEven()&&j)||f.signum()==0){return BigInteger.ZERO}var i=f.clone(),h=this.clone();var g=nbv(1),e=nbv(0),l=nbv(0),k=nbv(1);while(i.signum()!=0){while(i.isEven()){i.rShiftTo(1,i);if(j){if(!g.isEven()||!e.isEven()){g.addTo(this,g);e.subTo(f,e)}g.rShiftTo(1,g)}else{if(!e.isEven()){e.subTo(f,e)}}e.rShiftTo(1,e)}while(h.isEven()){h.rShiftTo(1,h);if(j){if(!l.isEven()||!k.isEven()){l.addTo(this,l);k.subTo(f,k)}l.rShiftTo(1,l)}else{if(!k.isEven()){k.subTo(f,k)}}k.rShiftTo(1,k)}if(i.compareTo(h)>=0){i.subTo(h,i);if(j){g.subTo(l,g)}e.subTo(k,e)}else{h.subTo(i,h);if(j){l.subTo(g,l)}k.subTo(e,k)}}if(h.compareTo(BigInteger.ONE)!=0){return BigInteger.ZERO}if(k.compareTo(f)>=0){return k.subtract(f)}if(k.signum()<0){k.addTo(f,k)}else{return k}if(k.signum()<0){return k.add(f)}else{return k}}var lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];var lplim=(1<<26)/lowprimes[lowprimes.length-1];function bnIsProbablePrime(e){var d,b=this.abs();if(b.t==1&&b[0]<=lowprimes[lowprimes.length-1]){for(d=0;d>1;if(f>lowprimes.length){f=lowprimes.length}var b=nbi();for(var e=0;e>8)&255;rng_pool[rng_pptr++]^=(a>>16)&255;rng_pool[rng_pptr++]^=(a>>24)&255;if(rng_pptr>=rng_psize){rng_pptr-=rng_psize}}function rng_seed_time(){rng_seed_int(new Date().getTime())}if(rng_pool==null){rng_pool=new Array();rng_pptr=0;var t;if(window!==undefined&&(window.crypto!==undefined||window.msCrypto!==undefined)){var crypto=window.crypto||window.msCrypto;if(crypto.getRandomValues){var ua=new Uint8Array(32);crypto.getRandomValues(ua);for(t=0;t<32;++t){rng_pool[rng_pptr++]=ua[t]}}else{if(navigator.appName=="Netscape"&&navigator.appVersion<"5"){var z=window.crypto.random(32);for(t=0;t>>8;rng_pool[rng_pptr++]=t&255}rng_pptr=0;rng_seed_time()}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr=0&&h>0){var f=e.charCodeAt(d--);if(f<128){g[--h]=f}else{if((f>127)&&(f<2048)){g[--h]=(f&63)|128;g[--h]=(f>>6)|192}else{g[--h]=(f&63)|128;g[--h]=((f>>6)&63)|128;g[--h]=(f>>12)|224}}}g[--h]=0;var b=new SecureRandom();var a=new Array();while(h>2){a[0]=0;while(a[0]==0){b.nextBytes(a)}g[--h]=a[0]}g[--h]=2;g[--h]=0;return new BigInteger(g)}function oaep_mgf1_arr(c,a,e){var b="",d=0;while(b.length>24,(d&16711680)>>16,(d&65280)>>8,d&255])));d+=1}return b}function oaep_pad(q,a,f,l){var c=KJUR.crypto.MessageDigest;var o=KJUR.crypto.Util;var b=null;if(!f){f="sha1"}if(typeof f==="string"){b=c.getCanonicalAlgName(f);l=c.getHashLength(b);f=function(i){return hextorstr(o.hashHex(rstrtohex(i),b))}}if(q.length+2*l+2>a){throw"Message too long for RSA"}var k="",e;for(e=0;e0&&a.length>0){this.n=parseBigInt(b,16);this.e=parseInt(a,16)}else{throw"Invalid RSA public key"}}}function RSADoPublic(a){return a.modPowInt(this.e,this.n)}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.type="RSA"; +/*! (c) Tom Wu, Kenji Urushima | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function pkcs1unpad2(g,j){var a=g.toByteArray();var f=0;while(f=a.length){return null}}var e="";while(++f191)&&(h<224)){e+=String.fromCharCode(((h&31)<<6)|(a[f+1]&63));++f}else{e+=String.fromCharCode(((h&15)<<12)|((a[f+1]&63)<<6)|(a[f+2]&63));f+=2}}}return e}function oaep_mgf1_str(c,a,e){var b="",d=0;while(b.length>24,(d&16711680)>>16,(d&65280)>>8,d&255]));d+=1}return b}function oaep_unpad(o,b,g,p){var e=KJUR.crypto.MessageDigest;var r=KJUR.crypto.Util;var c=null;if(!g){g="sha1"}if(typeof g==="string"){c=e.getCanonicalAlgName(g);p=e.getHashLength(c);g=function(d){return hextorstr(r.hashHex(rstrtohex(d),c))}}o=o.toByteArray();var h;for(h=0;h0&&a.length>0){this.n=parseBigInt(c,16);this.e=parseInt(a,16);this.d=parseBigInt(b,16)}else{throw"Invalid RSA private key"}}}function RSASetPrivateEx(g,d,e,c,b,a,h,f){this.isPrivate=true;this.isPublic=false;if(g==null){throw"RSASetPrivateEx N == null"}if(d==null){throw"RSASetPrivateEx E == null"}if(g.length==0){throw"RSASetPrivateEx N.length == 0"}if(d.length==0){throw"RSASetPrivateEx E.length == 0"}if(g!=null&&d!=null&&g.length>0&&d.length>0){this.n=parseBigInt(g,16);this.e=parseInt(d,16);this.d=parseBigInt(e,16);this.p=parseBigInt(c,16);this.q=parseBigInt(b,16);this.dmp1=parseBigInt(a,16);this.dmq1=parseBigInt(h,16);this.coeff=parseBigInt(f,16)}else{throw"Invalid RSA private key in RSASetPrivateEx"}}function RSAGenerate(b,l){var a=new SecureRandom();var g=b>>1;this.e=parseInt(l,16);var c=new BigInteger(l,16);var d=(b/2)-100;var k=BigInteger.ONE.shiftLeft(d);for(;;){for(;;){this.p=new BigInteger(b-g,1,a);if(this.p.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE)==0&&this.p.isProbablePrime(10)){break}}for(;;){this.q=new BigInteger(g,1,a);if(this.q.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE)==0&&this.q.isProbablePrime(10)){break}}if(this.p.compareTo(this.q)<=0){var j=this.p;this.p=this.q;this.q=j}var h=this.q.subtract(this.p).abs();if(h.bitLength()15){throw new Error("ASN.1 length too long to represent by 8x: n = "+j.toString(16))}var g=128+h;return g.toString(16)+i}};this.tohex=function(){if(this.hTLV==null||this.isModified){this.hV=this.getFreshValueHex();this.hL=this.getLengthHexFromValue();this.hTLV=this.hT+this.hL+this.hV;this.isModified=false}return this.hTLV};this.getEncodedHex=function(){return this.tohex()};this.getValueHex=function(){this.tohex();return this.hV};this.getFreshValueHex=function(){return""};this.setByParam=function(g){this.params=g};if(e!=undefined){if(e.tlv!=undefined){this.hTLV=e.tlv;this.isModified=false}}};KJUR.asn1.DERAbstractString=function(c){KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var b=null;var a=null;this.getString=function(){return this.s};this.setString=function(d){this.hTLV=null;this.isModified=true;this.s=d;this.hV=utf8tohex(this.s).toLowerCase()};this.setStringHex=function(d){this.hTLV=null;this.isModified=true;this.s=null;this.hV=d};this.getFreshValueHex=function(){return this.hV};if(typeof c!="undefined"){if(typeof c=="string"){this.setString(c)}else{if(typeof c.str!="undefined"){this.setString(c.str)}else{if(typeof c.hex!="undefined"){this.setStringHex(c.hex)}}}}};extendClass(KJUR.asn1.DERAbstractString,KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractTime=function(c){KJUR.asn1.DERAbstractTime.superclass.constructor.call(this);var b=null;var a=null;this.localDateToUTC=function(g){var e=g.getTime()+(g.getTimezoneOffset()*60000);var f=new Date(e);return f};this.formatDate=function(m,o,e){var g=this.zeroPadding;var n=this.localDateToUTC(m);var p=String(n.getFullYear());if(o=="utc"){p=p.substr(2,2)}var l=g(String(n.getMonth()+1),2);var q=g(String(n.getDate()),2);var h=g(String(n.getHours()),2);var i=g(String(n.getMinutes()),2);var j=g(String(n.getSeconds()),2);var r=p+l+q+h+i+j;if(e===true){var f=n.getMilliseconds();if(f!=0){var k=g(String(f),3);k=k.replace(/[0]+$/,"");r=r+"."+k}}return r+"Z"};this.zeroPadding=function(e,d){if(e.length>=d){return e}return new Array(d-e.length+1).join("0")+e};this.setByParam=function(d){this.hV=null;this.hTLV=null;this.params=d};this.getString=function(){return undefined};this.setString=function(d){this.hTLV=null;this.isModified=true;if(this.params==undefined){this.params={}}this.params.str=d};this.setByDate=function(d){this.hTLV=null;this.isModified=true;if(this.params==undefined){this.params={}}this.params.date=d};this.setByDateValue=function(h,j,e,d,f,g){var i=new Date(Date.UTC(h,j-1,e,d,f,g,0));this.setByDate(i)};this.getFreshValueHex=function(){return this.hV}};extendClass(KJUR.asn1.DERAbstractTime,KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractStructured=function(b){KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var a=null;this.setByASN1ObjectArray=function(c){this.hTLV=null;this.isModified=true;this.asn1Array=c};this.appendASN1Object=function(c){this.hTLV=null;this.isModified=true;this.asn1Array.push(c)};this.asn1Array=new Array();if(typeof b!="undefined"){if(typeof b.array!="undefined"){this.asn1Array=b.array}}};extendClass(KJUR.asn1.DERAbstractStructured,KJUR.asn1.ASN1Object);KJUR.asn1.DERBoolean=function(a){KJUR.asn1.DERBoolean.superclass.constructor.call(this);this.hT="01";if(a==false){this.hTLV="010100"}else{this.hTLV="0101ff"}};extendClass(KJUR.asn1.DERBoolean,KJUR.asn1.ASN1Object);KJUR.asn1.DERInteger=function(b){KJUR.asn1.DERInteger.superclass.constructor.call(this);this.hT="02";this.params=null;var a=twoscompl;this.setByBigInteger=function(c){this.isModified=true;this.params={bigint:c}};this.setByInteger=function(c){this.isModified=true;this.params=c};this.setValueHex=function(c){this.isModified=true;this.params={hex:c}};this.getFreshValueHex=function(){var d=this.params;var c=null;if(d==null){throw new Error("value not set")}if(typeof d=="object"&&d.hex!=undefined){this.hV=d.hex;return this.hV}if(typeof d=="number"){c=new BigInteger(String(d),10)}else{if(d["int"]!=undefined){c=new BigInteger(String(d["int"]),10)}else{if(d.bigint!=undefined){c=d.bigint}else{throw new Error("wrong parameter")}}}this.hV=a(c);return this.hV};if(b!=undefined){this.params=b}};extendClass(KJUR.asn1.DERInteger,KJUR.asn1.ASN1Object);KJUR.asn1.DERBitString=function(b){if(b!==undefined&&typeof b.obj!=="undefined"){var a=KJUR.asn1.ASN1Util.newObject(b.obj);b.hex="00"+a.tohex()}KJUR.asn1.DERBitString.superclass.constructor.call(this);this.hT="03";this.setHexValueIncludingUnusedBits=function(c){this.hTLV=null;this.isModified=true;this.hV=c};this.setUnusedBitsAndHexValue=function(c,e){if(c<0||7=f){break}}return j};ASN1HEX.getNthChildIdx=function(d,b,e){var c=ASN1HEX.getChildIdx(d,b);return c[e]};ASN1HEX.getIdxbyList=function(e,d,c,i){var g=ASN1HEX;var f,b;if(c.length==0){if(i!==undefined){if(e.substr(d,2)!==i){return -1}}return d}f=c.shift();b=g.getChildIdx(e,d);if(f>=b.length){return -1}return g.getIdxbyList(e,b[f],c,i)};ASN1HEX.getIdxbyListEx=function(f,k,b,g){var m=ASN1HEX;var d,l;if(b.length==0){if(g!==undefined){if(f.substr(k,2)!==g){return -1}}return k}d=b.shift();l=m.getChildIdx(f,k);var j=0;for(var e=0;e=d.length){return null}return e.getTLV(d,a)};ASN1HEX.getTLVbyListEx=function(d,c,b,f){var e=ASN1HEX;var a=e.getIdxbyListEx(d,c,b,f);if(a==-1){return null}return e.getTLV(d,a)};ASN1HEX.getVbyList=function(e,c,b,g,i){var f=ASN1HEX;var a,d;a=f.getIdxbyList(e,c,b,g);if(a==-1){return null}if(a>=e.length){return null}d=f.getV(e,a);if(i===true){d=d.substr(2)}return d};ASN1HEX.getVbyListEx=function(b,e,a,d,f){var j=ASN1HEX;var g,c,i;g=j.getIdxbyListEx(b,e,a,d);if(g==-1){return null}i=j.getV(b,g);if(b.substr(g,2)=="03"&&f!==false){i=i.substr(2)}return i};ASN1HEX.getInt=function(e,b,f){if(f==undefined){f=-1}try{var c=e.substr(b,2);if(c!="02"&&c!="03"){return f}var a=ASN1HEX.getV(e,b);if(c=="02"){return parseInt(a,16)}else{return bitstrtoint(a)}}catch(d){return f}};ASN1HEX.getOID=function(c,a,d){if(d==undefined){d=null}try{if(c.substr(a,2)!="06"){return d}var e=ASN1HEX.getV(c,a);return hextooid(e)}catch(b){return d}};ASN1HEX.getOIDName=function(d,a,f){if(f==undefined){f=null}try{var e=ASN1HEX.getOID(d,a,f);if(e==f){return f}var b=KJUR.asn1.x509.OID.oid2name(e);if(b==""){return e}return b}catch(c){return f}};ASN1HEX.getString=function(d,b,e){if(e==undefined){e=null}try{var a=ASN1HEX.getV(d,b);return hextorstr(a)}catch(c){return e}};ASN1HEX.hextooidstr=function(e){var h=function(b,a){if(b.length>=a){return b}return new Array(a-b.length+1).join("0")+b};var l=[];var o=e.substr(0,2);var f=parseInt(o,16);l[0]=new String(Math.floor(f/40));l[1]=new String(f%40);var m=e.substr(2);var k=[];for(var g=0;g0){n=n+"."+j.join(".")}return n};ASN1HEX.dump=function(t,c,l,g){var p=ASN1HEX;var j=p.getV;var y=p.dump;var w=p.getChildIdx;var e=t;if(t instanceof KJUR.asn1.ASN1Object){e=t.tohex()}var q=function(A,i){if(A.length<=i*2){return A}else{var v=A.substr(0,i)+"..(total "+A.length/2+"bytes).."+A.substr(A.length-i,i);return v}};if(c===undefined){c={ommit_long_octet:32}}if(l===undefined){l=0}if(g===undefined){g=""}var x=c.ommit_long_octet;var z=e.substr(l,2);if(z=="01"){var h=j(e,l);if(h=="00"){return g+"BOOLEAN FALSE\n"}else{return g+"BOOLEAN TRUE\n"}}if(z=="02"){var h=j(e,l);return g+"INTEGER "+q(h,x)+"\n"}if(z=="03"){var h=j(e,l);if(p.isASN1HEX(h.substr(2))){var k=g+"BITSTRING, encapsulates\n";k=k+y(h.substr(2),c,0,g+" ");return k}else{return g+"BITSTRING "+q(h,x)+"\n"}}if(z=="04"){var h=j(e,l);if(p.isASN1HEX(h)){var k=g+"OCTETSTRING, encapsulates\n";k=k+y(h,c,0,g+" ");return k}else{return g+"OCTETSTRING "+q(h,x)+"\n"}}if(z=="05"){return g+"NULL\n"}if(z=="06"){var m=j(e,l);var b=KJUR.asn1.ASN1Util.oidHexToInt(m);var o=KJUR.asn1.x509.OID.oid2name(b);var a=b.replace(/\./g," ");if(o!=""){return g+"ObjectIdentifier "+o+" ("+a+")\n"}else{return g+"ObjectIdentifier ("+a+")\n"}}if(z=="0a"){return g+"ENUMERATED "+parseInt(j(e,l))+"\n"}if(z=="0c"){return g+"UTF8String '"+hextoutf8(j(e,l))+"'\n"}if(z=="13"){return g+"PrintableString '"+hextoutf8(j(e,l))+"'\n"}if(z=="14"){return g+"TeletexString '"+hextoutf8(j(e,l))+"'\n"}if(z=="16"){return g+"IA5String '"+hextoutf8(j(e,l))+"'\n"}if(z=="17"){return g+"UTCTime "+hextoutf8(j(e,l))+"\n"}if(z=="18"){return g+"GeneralizedTime "+hextoutf8(j(e,l))+"\n"}if(z=="1a"){return g+"VisualString '"+hextoutf8(j(e,l))+"'\n"}if(z=="1e"){return g+"BMPString '"+ucs2hextoutf8(j(e,l))+"'\n"}if(z=="30"){if(e.substr(l,4)=="3000"){return g+"SEQUENCE {}\n"}var k=g+"SEQUENCE\n";var d=w(e,l);var f=c;if((d.length==2||d.length==3)&&e.substr(d[0],2)=="06"&&e.substr(d[d.length-1],2)=="04"){var o=p.oidname(j(e,d[0]));var r=JSON.parse(JSON.stringify(c));r.x509ExtName=o;f=r}for(var u=0;u4){return{"enum":{hex:p}}}else{return{"enum":parseInt(p,16)}}}else{if(C=="30"||C=="31"){j[c[C]]=u(x);return j}else{if(C=="14"){var o=q(p);j[c[C]]={str:o};return j}else{if(C=="1e"){var o=n(p);j[c[C]]={str:o};return j}else{if(":0c:12:13:16:17:18:1a:".indexOf(C)!=-1){var o=k(p);j[c[C]]={str:o};return j}else{if(C.match(/^8[0-9]$/)){var o=k(p);if(o==null|o==""){return{tag:{tag:C,explicit:false,hex:p}}}else{if(o.match(/[\x00-\x1F\x7F-\x9F]/)!=null||o.match(/[\u0000-\u001F\u0080–\u009F]/)!=null){return{tag:{tag:C,explicit:false,hex:p}}}else{return{tag:{tag:C,explicit:false,str:o}}}}}else{if(C.match(/^a[0-9]$/)){try{if(!a(p)){throw new Error("not encap")}return{tag:{tag:C,explicit:true,obj:f(p)}}}catch(z){return{tag:{tag:C,explicit:true,hex:p}}}}else{var A=new KJUR.asn1.ASN1Object();A.hV=p;var w=A.getLengthHexFromValue();return{asn1:{tlv:C+w+p}}}}}}}}}}}}}}}};ASN1HEX.isContextTag=function(c,b){c=c.toLowerCase();var f,e;try{f=parseInt(c,16)}catch(d){return -1}if(b===undefined){if((f&192)==128){return true}else{return false}}try{var a=b.match(/^\[[0-9]+\]$/);if(a==null){return false}e=parseInt(b.substr(1,b.length-1),10);if(e>31){return false}if(((f&192)==128)&&((f&31)==e)){return true}return false}catch(d){return false}};ASN1HEX.isASN1HEX=function(e){var d=ASN1HEX;if(e.length%2==1){return false}var c=d.getVblen(e,0);var b=e.substr(0,2);var f=d.getL(e,0);var a=e.length-b.length-f.length;if(a==c*2){return true}return false};ASN1HEX.checkStrictDER=function(g,o,d,c,r){var s=ASN1HEX;if(d===undefined){if(typeof g!="string"){throw new Error("not hex string")}g=g.toLowerCase();if(!KJUR.lang.String.isHex(g)){throw new Error("not hex string")}d=g.length;c=g.length/2;if(c<128){r=1}else{r=Math.ceil(c.toString(16))+1}}var k=s.getL(g,o);if(k.length>r*2){throw new Error("L of TLV too long: idx="+o)}var n=s.getVblen(g,o);if(n>c){throw new Error("value of L too long than hex: idx="+o)}var q=s.getTLV(g,o);var f=q.length-2-s.getL(g,o).length;if(f!==(n*2)){throw new Error("V string length and L's value not the same:"+f+"/"+(n*2))}if(o===0){if(g.length!=q.length){throw new Error("total length and TLV length unmatch:"+g.length+"!="+q.length)}}var b=g.substr(o,2);if(b==="02"){var a=s.getVidx(g,o);if(g.substr(a,2)=="00"&&g.charCodeAt(a+2)<56){throw new Error("not least zeros for DER INTEGER")}}if(parseInt(b,16)&32){var p=s.getVblen(g,o);var m=0;var l=s.getChildIdx(g,o);for(var e=0;e>6);var i=128|(a&63);return hextoutf8(j.toString(16)+i.toString(16))}var j=224|((h&240)>>4);var i=128|((h&15)<<2)|((a&192)>>6);var g=128|(a&63);return hextoutf8(j.toString(16)+i.toString(16)+g.toString(16))}var c=d.match(/.{4}/g);var b=c.map(e);return b.join("")}function encodeURIComponentAll(a){var d=encodeURIComponent(a);var b="";for(var c=0;c"7"){return"00"+a}return a}function intarystrtohex(b){b=b.replace(/^\s*\[\s*/,"");b=b.replace(/\s*\]\s*$/,"");b=b.replace(/\s*/g,"");try{var c=b.split(/,/).map(function(g,e,h){var f=parseInt(g);if(f<0||255a.length){d=a.length}for(var b=0;b0){o=o+"."+k.join(".")}return o}catch(j){return null}}function inttohex(b){var a=new BigInteger(String(b),10);return twoscompl(a)}function twoscompl(b){var g=b.toString(16);if(g.substr(0,1)!="-"){if(g.length%2==1){g="0"+g}else{if(!g.match(/^[0-7]/)){g="00"+g}}return g}var a=g.substr(1);var f=a.length;if(f%2==1){f+=1}else{if(!g.match(/^[0-7]/)){f+=2}}var j="";for(var e=0;e=b){return c}return new Array(b-c.length+1).join(a)+c};function bitstrtoint(e){if(e.length%2!=0){return -1}e=e.toLowerCase();if(e.match(/^[0-9a-f]+$/)==null){return -1}try{var a=e.substr(0,2);if(a=="00"){return parseInt(e.substr(2),16)}var b=parseInt(a,16);if(b>7){return -1}var g=e.substr(2);var d=parseInt(g,16).toString(2);if(d=="0"){d="00000000"}d=d.slice(0,0-b);var f=parseInt(d,2);if(f==NaN){return -1}return f}catch(c){return -1}}function inttobitstr(e){if(typeof e!="number"){return null}if(e<0){return null}var c=Number(e).toString(2);var b=8-c.length%8;if(b==8){b=0}c=c+strpad("",b,"0");var d=parseInt(c,2).toString(16);if(d.length%2==1){d="0"+d}var a="0"+b;return a+d}function bitstrtobinstr(g){if(typeof g!="string"){return null}if(g.length%2!=0){return null}if(!g.match(/^[0-9a-f]+$/)){return null}try{var c=parseInt(g.substr(0,2),16);if(c<0||7=0;a--){c+=b[a]}return c}function aryval(e,c,d){if(typeof e!="object"){return undefined}var c=String(c).split(".");for(var b=0;bd){throw"key is too short for SigAlg: keylen="+j+","+a}var b="0001";var k="00"+c;var g="";var l=d-b.length-k.length;for(var f=0;f>24,(d&16711680)>>16,(d&65280)>>8,d&255]))));d+=1}return b}RSAKey.prototype.signPSS=function(e,a,d){var c=function(f){return KJUR.crypto.Util.hashHex(f,a)};var b=c(rstrtohex(e));if(d===undefined){d=-1}return this.signWithMessageHashPSS(b,a,d)};RSAKey.prototype.signWithMessageHashPSS=function(l,a,k){var b=hextorstr(l);var g=b.length;var m=this.n.bitLength()-1;var c=Math.ceil(m/8);var d;var o=function(i){return KJUR.crypto.Util.hashHex(i,a)};if(k===-1||k===undefined){k=g}else{if(k===-2){k=c-g-2}else{if(k<-2){throw new Error("invalid salt length")}}}if(c<(g+k+2)){throw new Error("data too long")}var f="";if(k>0){f=new Array(k);new SecureRandom().nextBytes(f);f=String.fromCharCode.apply(String,f)}var n=hextorstr(o(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00"+b+f)));var j=[];for(d=0;d>(8*c-m))&255;q[0]&=~p;for(d=0;dk){return false}var j=this.doPublic(b);var i=j.toString(16);if(i.length+3!=k/4){return false}var e=i.replace(/^1f+00/,"");var g=_rsasign_getAlgNameAndHashFromHexDisgestInfo(e);if(g.length==0){return false}var d=g[0];var h=g[1];var a=function(m){return KJUR.crypto.Util.hashString(m,d)};var c=a(f);return(h==c)};RSAKey.prototype.verifyWithMessageHash=function(e,a){if(a.length!=Math.ceil(this.n.bitLength()/4)){return false}var b=parseBigInt(a,16);if(b.bitLength()>this.n.bitLength()){return 0}var h=this.doPublic(b);var g=h.toString(16).replace(/^1f+00/,"");var c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(g);if(c.length==0){return false}var d=c[0];var f=c[1];return(f==e)};RSAKey.prototype.verifyPSS=function(c,b,a,f){var e=function(g){return KJUR.crypto.Util.hashHex(g,a)};var d=e(rstrtohex(c));if(f===undefined){f=-1}return this.verifyWithMessageHashPSS(d,b,a,f)};RSAKey.prototype.verifyWithMessageHashPSS=function(f,s,l,c){if(s.length!=Math.ceil(this.n.bitLength()/4)){return false}var k=new BigInteger(s,16);var r=function(i){return KJUR.crypto.Util.hashHex(i,l)};var j=hextorstr(f);var h=j.length;var g=this.n.bitLength()-1;var m=Math.ceil(g/8);var q;if(c===-1||c===undefined){c=h}else{if(c===-2){c=m-h-2}else{if(c<-2){throw new Error("invalid salt length")}}}if(m<(h+c+2)){throw new Error("data too long")}var a=this.doPublic(k).toByteArray();for(q=0;q>(8*m-g))&255;if((d.charCodeAt(0)&p)!==0){throw new Error("bits beyond keysize not zero")}var n=pss_mgf1_str(e,d.length,r);var o=[];for(q=0;q> 2; + const enc2 = ((char1 & 3) << 4) | (isNaN(char2) ? 0 : char2 >> 4); + const enc3 = isNaN(char2) ? 64 : ((char2 & 15) << 2) | (isNaN(char3) ? 0 : char3 >> 6); + const enc4 = isNaN(char3) ? 64 : (char3 & 63); + + result += this.base64Chars.charAt(enc1) + + this.base64Chars.charAt(enc2) + + this.base64Chars.charAt(enc3) + + this.base64Chars.charAt(enc4); + } + + return result; + }, + HexToBase64: function(hexstring){ + // clean hex + input = hexstring.toUpperCase(); + var orig_input = hexstring; + cleaned_hex = input.replace(/[^A-Fa-f0-9]/g, ""); + //if (orig_input != input) + // return ("Warning! Non-hex characters in input string ignored."); + var binary = new Array(); + for (var index=0; index> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (i = 0; (i <4) ; i++) + ret += this.base64Chars.charAt(char_array_4[i]); + i = 0; + } + } + + if (i) + { + for (j = i; j < 3; j++) + char_array_3[j] = 0; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += this.base64Chars.charAt(char_array_4[j]); + + while ((i++ < 3)) + ret += '='; + } + return ret; + }, + Create_evidence: function(jfrog_platform_url, jfrog_evidence_pkey, jfrog_keyid, jfrog_bearer, evidence_subject, evidence_payload) { + // 1: get base64 of payload + base64evidence_payload = this.GetBase64EncodedStr(evidence_payload); + //gs.info('base64evidence_payload='+base64evidence_payload); + // 2. create and sign PAE + const payloadType = 'application/vnd.in-toto+json'; + const pae='DSSEv1 ' + payloadType.length + ' '+payloadType + ' ' + evidence_payload.length+' '+evidence_payload; + const hashAlg = 'sha256'; + + //CryptoJS signing process + var rsa = new RSAKey(); + if (!rsa ) {return "rsa was null";} + rsa.readPrivateKeyFromPEMString(jfrog_evidence_pkey); + var hSig = rsa.sign(pae, hashAlg); + //gs.info('hSig='+hSig); + if (!hSig ) {return "hSig was null";} + signed_based_pae = this.HexToBase64(hSig); + //gs.info('signed_based_pae='+signed_based_pae); + // 3. evidence creation + var create_evidence = new sn_ws.RESTMessageV2('x_1560608_jfrog_1.JFrog_evidence', 'create_evidence'); + var create_evidence_endpoint = jfrog_platform_url+'/evidence/api/v1/subject/'+evidence_subject; + //gs.info('create_evidence_endpoint='+create_evidence_endpoint); + // override dsse envelope content + create_evidence.setEndpoint(create_evidence_endpoint); + create_evidence.setStringParameterNoEscape('payload',base64evidence_payload); + create_evidence.setStringParameterNoEscape('keyid',jfrog_keyid); + create_evidence.setStringParameterNoEscape('signature',signed_based_pae); + create_evidence.setRequestHeader('Authorization','Bearer '+jfrog_bearer); + var create_evidence_response = create_evidence.execute(); + var create_evidence_response_body = create_evidence_response.getBody(); + var create_evidence_http_status = create_evidence_response.getStatusCode(); + create_evidence_response_obj = JSON.parse(create_evidence_response_body); + var status = (create_evidence_http_status== "201" ); + return { + success: status, + http_status: create_evidence_http_status, + response_obj: create_evidence_response_obj + }; + }, + type: 'JFrogEvidenceOperations' +}; \ No newline at end of file diff --git a/examples/service-now evidence/JFrogReleaseOperations b/examples/service-now evidence/JFrogReleaseOperations new file mode 100644 index 0000000..6b39820 --- /dev/null +++ b/examples/service-now evidence/JFrogReleaseOperations @@ -0,0 +1,49 @@ +var JFrogReleaseOperations = Class.create(); +JFrogReleaseOperations.prototype = { + initialize: function(jfrog_platform_url, jfrog_bearer) { + this.jfrog_platform_url = jfrog_platform_url; + this.jfrog_bearer = jfrog_bearer; + }, + Promote: function( jfrog_release_name, jfrog_release_number, target_env, proj_key) { + var promote_request = new sn_ws.RESTMessageV2('x_1560608_jfrog_1.JFrog_rb', 'promote'); + // override endpoint target + var endpoint = this.jfrog_platform_url + '/lifecycle/api/v2/promotion/records/' + jfrog_release_name + '/' + jfrog_release_number; + + gs.info("Will promote " + current.number + " through endpoint " + endpoint); + promote_request.setEndpoint(endpoint); + // verride body vars + promote_request.setStringParameterNoEscape('env', target_env); + promote_request.setStringParameterNoEscape('project', proj_key); + promote_request.setRequestHeader('Authorization', 'Bearer ' + this.jfrog_bearer); + // call promotion api + var response = promote_request.execute(); + var responseBody = response.getBody(); + var httpStatus = response.getStatusCode(); + responseObj = JSON.parse(responseBody); + var status = (httpStatus == "201"); + return { + promotion_success: status, + promotion_http_status: httpStatus, + promotion_response_obj: responseObj + }; + + }, + Get_release_info: function( jfrog_release_name, jfrog_release_number, rb_repo) { + var get_release_info = new sn_ws.RESTMessageV2('x_1560608_jfrog_1.jfrog_storage', 'get_release_info'); + var storage_endpoint = this.jfrog_platform_url+'/artifactory/api/storage/'+rb_repo+jfrog_release_name+"/"+jfrog_release_number+"/release-bundle.json.evd"; + get_release_info.setEndpoint(storage_endpoint); + get_release_info.setRequestHeader('Authorization', 'Bearer ' + this.jfrog_bearer); + // call get info api + var get_release_info_response = get_release_info.execute(); + var get_release_info_responseBody = get_release_info_response.getBody(); + var get_release_info_httpStatus = get_release_info_response.getStatusCode(); + responseObj = JSON.parse(get_release_info_responseBody); + var status = (get_release_info_httpStatus == "200"); + return { + get_release_info_success: status, + get_release_info_httpStatus: get_release_info_httpStatus, + get_release_info_responseObj: responseObj + }; + }, + type: 'JFrogReleaseOperations' +}; \ No newline at end of file diff --git a/examples/service-now evidence/README.md b/examples/service-now evidence/README.md new file mode 100644 index 0000000..17a05a4 --- /dev/null +++ b/examples/service-now evidence/README.md @@ -0,0 +1,103 @@ +# Create Evidence inside Servicenow and upload them into Artifactory +ServiceNow offers multiple processes in which a user would benefit from creating a JFrog evidence. +For example, following a release approval step, i.e., approval to promotion a release bundle or a package, approval to deploy a new version or to distribute a release version. + +These approval flows, being completed on servicenow, trigger operations inside JFrog, but without generating the evidence on servicenow, the release operations may not be backtracked for compliance or verified by automated controls. + +This example demonstrates how to create evidence in servicenow and upload them into Artifactory. + +Fow integrating servicenow with JFrog, we will add few servicenow components that can then be used as building blocks in any servicenow process that required a JFrog evidence. + +Notice, throughout the example scripts referencing the added components will use the x_1560608_jfrog_1 app id which should be replaced with your own app id. + +## Example of a servicenow business rule +The business rule in this example is planned to be triggered following a servicenow change request approval for a promotion request. +The script promotes a release bundle and creates an evidence of the promotion approval. + +business rule script example is on [jfrog_promotion](jfrog_promotion) +Notice this script requires the below mandatory and optional servicenow entities created. +Notice that a few elements were not added here, for example, the notification and the event registry (eventQueue) that handle the business rules failures (as these are async and so cannot be handled through the UI). +While for other scenarios you might only need to create the mandatory components that perform evidence creation, signing and uploading, this business rule script performs a full process of promotion and evidence creation and so requires also few optional elements. + +## Mandatory Servicenow components required for the integration: +### 1. Script includes: +- **CryptoJS**: see [CryptoJS](CryptoJS) adds Cryptographic functions to servicenow, based on https://github.com/kjur/jsrsasign (MIT License). +- **JFrogEvidenceOperations**: see [JFrogEvidenceOperations](JFrogEvidenceOperations) which uses the CryptoJS for signing operations and adds few other evidence related utilities such as base64 handling and evidence creation. the main function is Create_evidence function. + - Create_evidence function input parameters: + - jfrog_platform_url: the JFrog platform url (for example mycompany.jfrog.io) + - jfrog_evidence_pkey: private key content that is used for the signing + - jfrog_keyid: optional, the JFrog name of the public key that was uploaded to jfrog, if missing JFRog signature verification will not be performed + - jfrog_bearer: JFrog bearer token + - evidence_subject: the digest of the evidence subject, this may be the digest of a release bundle, a package, a buildinfo or any other JFrog artifact that the evidence is related to. + - evidence_payload: predicate of the evidence, it is advised to create a system proprty with a evidence templates that can be used for generating this content. + - result json: + ```json + { + success: status, + http_status: create_evidence_http_status, + response_obj: create_evidence_response_obj + } + ``` +- + +### 2. Outbound Integrations > Rest Msssages +- **JFrog_evidence**: an outbound rest api for posting evidence to JFrog. + - **Rest message Endpoint**: `https://$jfrog_platform_url.jfrog.io` + - **HTTP Method**: create_evidence + - ***Endpoint***: POST `https://${jfrog_platform_url}/evidence/api/v1/subject/${project}-release-bundles-v2/${release_name}/${release_number}/release-bundle.json.evd` + - **Request Body**: + ```json + { + "payload": "${payload}", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "keyid": "${keyid}", + "sig": "${signature}" + } + ] + } + ``` + + +## Optiopnal Servicenow components required for the integration: +### 1. Script includes: +- **JFrogReleaseOperations** see [JFrogReleaseOperations](JFrogReleaseOperations) provides functions for getting a release bundle digest and for promoting a release bundle. +### 2. Outbound Integrations > Rest Msssages +- **JFrog_rb**: an outbound rest api for working with JFrog Release bundle . + - **Rest message Endpoint**: `https://${jfrog_platform_url}.jfrog.io/lifecycle/` + - **HTTP Method**: promote + - ***Endpoint***: POST `${jfrog_platform_url}/lifecycle/api/v2/promotion/records/my-rb/1.0.1` + - **Query Parameters**: + - ***name***: project ***Value*** : ${project} + - ***name***: async ***Value*** : false + - **Request Body**: + ```json + { + "environment": "${env}", + "included_repository_keys": [], + "excluded_repository_keys": [] + } + ``` + +- **JFrog_rb**: an outbound rest api for working with JFrog storage, in our example, it is used for getting a release bundle digest. + - **Rest message Endpoint**: `https://${jfrog_platform_url}//artifactory/api/storage` + - **HTTP Method**: get_release_info + - ***Endpoint***: GET `https://${jfrog_platform_url}/artifactory/api/storage/${project}-release-bundles-v2/${release_name}/${release_number}/release-bundle.json.evd` + - **Query Parameters**: + - ***name***: project ***Value*** : ${project} + - ***name***: async ***Value*** : false + - **Request Body**: + ```json + { + "environment": "${env}", + "included_repository_keys": [], + "excluded_repository_keys": [] + } + ``` +### 3. system properties: +- ***jfrog_bearer***: JFrog bearer token, property of type password2 +- ***jfrog_evidence_pkey***: private key used for signing service now evidence, property of type password2 +- ***jfrog_platform_url***: JFrog platform url, property of type string +- ***JFrog_evidence_keyid***: JFrog public key id, property of type string +- ***\evidence-template***: a system property that holds template for evidence predicate content, this can be used for managing and generating the evidence payload. \ No newline at end of file diff --git a/examples/service-now evidence/jfrog_promotion b/examples/service-now evidence/jfrog_promotion new file mode 100644 index 0000000..edecbe1 --- /dev/null +++ b/examples/service-now evidence/jfrog_promotion @@ -0,0 +1,152 @@ +(function executeRule(current, previous /*null when async*/) { + /* + this function handles promotion of release bundle v2 and creates an evidence + the function requires set properties of: + x_1560608_jfrog_1.jfrog_platform_url + x_1560608_jfrog_1.promotion-evidence-template + x_1560608_jfrog_1.jfrog_evidence_pkey + x_1560608_jfrog_1.jfrog_bearer //password2 + x_1560608_jfrog_1.JFrog_evidence_keyid + + this script assumes that the current record is a change request of a custom type release promotion that has a few extra jfrog related data elements, such as: jfrog project key, release name and number and target promotion environment + */ + + let jfrog_bearer = gs.getProperty('x_1560608_jfrog_1.jfrog_bearer'); + let jfrog_platform_url = gs.getProperty('x_1560608_jfrog_1.jfrog_platform_url'); + + release_operations = new x_1560608_jfrog_1.JFrogReleaseOperations(jfrog_platform_url,jfrog_bearer ); + evidence_operations = new x_1560608_jfrog_1.JFrogEvidenceOperations(); + var promotion_result =''; + + try { + var promote = true; + gs.info("Will promote "+ current.number + " with realease "+ current.jfrog_release_name + ":" + current.jfrog_release_number + " and project " + current.jfrog_project); + + // handling project values + // some endpoints need default as project key, others require empty strings + var proj_key = current.jfrog_project; + if (proj_key == 'undefined'){proj_key='default';} + // rb_repo will be used for api calls with optional project key as part of rbv2 repo + var rb_repo = ''; + if (proj_key=='default'){ + rb_repo = 'release-bundles-v2/'; + }else { + rb_repo=proj_key+'-release-bundles-v2/'; + } + + // 1. perform promotion operation ------------- + const promotion_res = release_operations.Promote(current.jfrog_release_name, current.jfrog_release_number, current.env, proj_key); + promotion_success = promotion_res.promotion_success; + promotion_http_status = promotion_res.promotion_http_status; + promotion_response_obj = promotion_res.promotion_response_obj; + + if (promotion_success) { + promotion_result = 'Promotion done'; + gs.info("Promote of "+ current.number + " with release "+ current.jfrog_release_name + ":" + current.jfrog_release_number + " to env " + current.env + " completed with successful response"); + + // set change request state to review + current.state = 0; + + // evidence creation + // get release bundle sha256 digest + const release_info_res = release_operations.Get_release_info( current.jfrog_release_name, current.jfrog_release_number, rb_repo); + get_release_info_success = release_info_res.get_release_info_success; + get_release_info_httpStatus = release_info_res.get_release_info_httpStatus; + get_release_info_responseObj = release_info_res.get_release_info_responseObj; + + if (!get_release_info_success || ! get_release_info_responseObj || !get_release_info_responseObj.checksums || !get_release_info_responseObj.checksums.sha256) { + // handle response errors + var info_res_message = "NA"; + if (get_release_info_responseObj && get_release_info_responseObj.errors && get_release_info_responseObj.errors[0] && get_release_info_responseObj.errors[0].message){ + info_res_message=get_release_info_responseObj.errors[0].message; + } + gs.error("Promote of "+ current.number + " with realease "+ current.jfrog_release_name + ":" + current.jfrog_release_number + " to env " + current.env + " evidence creation failed during RB get checksum with status=" +get_release_info_httpStatus + " and error="+ info_res_message); + promotion_result+=' Failed getting release bundle checksum, so evidence is not created, check the application logs for more information'; + }else { + //extract the rb digest for the evidence + var sha256 = get_release_info_responseObj.checksums.sha256; + + // get evidence payload template and key properties + let evidence_payload = gs.getProperty('x_1560608_jfrog_1.promotion-evidence-template'); + let jfrog_evidence_pkey = gs.getProperty('x_1560608_jfrog_1.jfrog_evidence_pkey'); + let jfrog_keyid = gs.getProperty('x_1560608_jfrog_1.JFrog_evidence_keyid'); + if (!jfrog_keyid) {jfrog_keyid='';} // jfrog keyid is not mandatory, if not provided, the evidence is not verified on jfrog platform side + + if (!evidence_payload || !jfrog_evidence_pkey || !jfrog_bearer){ + gs.error("Promote of "+ current.number + " with release "+ current.jfrog_release_name + ":" + current.jfrog_release_number + " to env " + current.env + " evidence creation failed due to missing system properties: x_1560608_jfrog_1.promotion-evidence-template, x_1560608_jfrog_1.jfrog_evidence_pkey, x_1560608_jfrog_1.jfrog_bearer"); + promotion_result+=', evidence creation failed due to missing evidence template, private key or jfrog_bearer, check application logs for more information'; + } else { + // craft predicate payload from template + evidence_payload = evidence_payload.replaceAll("$digest",sha256 ); + evidence_payload = evidence_payload.replaceAll("$cr-number",current.number); + evidence_payload = evidence_payload.replaceAll("$release-name",current.jfrog_release_name); + evidence_payload = evidence_payload.replaceAll("$release-version",current.jfrog_release_number); + evidence_payload = evidence_payload.replaceAll("$target-environment",current.env); + evidence_payload = evidence_payload.replaceAll("$target-environment",current.env); + var gdt = new GlideDateTime(); + var isoFormat = gdt.getValue().replace(" ", "T") + ".000Z"; // Convert to ISO 8601 format + evidence_payload = evidence_payload.replaceAll("$timestamp",isoFormat); + evidence_payload = evidence_payload.replaceAll("$createdtimestamp",isoFormat); + gs.info('evidence_payload='+evidence_payload); + var evidence_subject = rb_repo+current.jfrog_release_name+"/"+current.jfrog_release_number+"/release-bundle.json.evd"; + + // create evidence + const evidence_result = evidence_operations.Create_evidence(jfrog_platform_url, jfrog_evidence_pkey, jfrog_keyid, jfrog_bearer, evidence_subject, evidence_payload); + success = evidence_result.success; + http_status = evidence_result.http_status; + response_obj = evidence_result.response_obj; + + if (!success ){ + // evidence creation failed + promotion_result+= ', Evidence creation failure, check application logs for more information'; + var create_evidence_res_message = "NA"; + if (response_obj && response_obj.errors && response_obj.errors[0] && response_obj.errors[0].message){ + create_evidence_res_message=response_obj.errors[0].message; + } + gs.error("evidence creation failed with status=" + http_status + " and error="+ create_evidence_res_message); + } else { + // evidence created + gs.info('evidence creation was successful'); + if (response_obj && response_obj.uri ){ + current.jfrog_promotion_evidence = gs.getProperty('x_1560608_jfrog_1.jfrog_platform_url')+'/artifactory/'+response_obj.uri; + } + promotion_result+= ', Evidence Created'; + } + } + } + } else { + // extract promotion response message + var res_message = "NA"; + if (promotion_response_obj && promotion_response_obj.errors && promotion_response_obj.errors[0] && promotion_response_obj.errors[0].message){ + res_message=promotion_response_obj.errors[0].message; + } + gs.error("Promote of "+ current.number + " with release "+ current.jfrog_release_name + ":" + current.jfrog_release_number + " to env " + current.env + " FAILED response= " + res_message); + // send error notification + gs.eventQueue('x_1560608_jfrog_1.jfrog_act_event', current, res_message); + // log notification sending + gs.info('promotion failure, error event fired: x_1560608_jfrog_1.jfrog_act_event' ); + + // updating the request with the result failure + current.promotion_result = 'Promotion Failed with message: '+res_message; + } + }catch(ex) { + gs.error("Promote of "+ current.number + " with release "+ current.jfrog_release_name + ":" + current.jfrog_release_number + " to env " + current.env + " failed with exception: " + ex.message ); + // send error notification, comment out if not needed or if notification and event components were not created + gs.eventQueue('x_1560608_jfrog_1.jfrog_act_event', current, 'Exception: '+ex.message ); + // log notification sending + gs.info('promotion failure, error event fired: x_1560608_jfrog_1.jfrog_act_event' ); + + // not sure this is helpful + current.state.setError('Promotion operation failed with exception ' + ex.message) ; + // updating the request with the result failure + current.promotion_result += ', Promotion flow issues, Exception: '+ex.message; + + } + // updating current record + current.promotion_result = promotion_result; + //gs.info('promotion completed, updating current record' ); + current.setWorkflow(false); + current.update(); + current.setWorkflow(true); + +})(current, previous); \ No newline at end of file diff --git a/examples/sonar-scan-example/README.md b/examples/sonar-scan-example/README.md new file mode 100644 index 0000000..d2770de --- /dev/null +++ b/examples/sonar-scan-example/README.md @@ -0,0 +1,56 @@ +# Create Sonar Scan Evidence predicate from the build CI and attach it to the build info +Sonar is a code scanning tool that helps to identify bugs, vulnerabilities, and code smells in your code. +It is important to track the code quality and security of the code changes done and released. +To allow automation of proper code quality and security checks, we create an evidence of the Sonar scan results +during the build with confirmation that the code quality and security checks passed before the code was committed. +using the `FailOnAnalysisFailure` argument the customer can decide if to create the sonar scan evidence if the scan did not pass +sonar quality gates, or fail the predicate creation with exist status 1. +If the Analysis status is not 'OK', but `FailOnAnalysisFailure` was not set, then the predicate is created with analysis.status = 'ERROR' which +should be checked using a policy. + +## Environment variables +- `SONAR_TOKEN` - The sonar server token. + +## Arguments +`--reportTaskFile=` - The path to the sonar report task file. + +`--FailOnAnalysisFailure` - Fail with exit code 1 if the sonar analysis failed in sonar quality gate. + + +## The example is based on the following steps: +1. set sonar token as an environment variable +2. call sonar scan +for example: +`` +$PWD/sonar-scanner-6.2.1.4610/bin/sonar-scanner \ + -Dsonar.projectKey=my-sonar-project-key \ + -Dsonar.organization=my-sonar-org \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.java.jdkHome=$JAVA_HOME \ + -Dsonar.verbose=true \ + -Dsonar.token=$SONAR_TOKEN +`` +3. call the jira-transition-checker utility (use the binary for your build platform) with these arguments: "transition name" JIRA-ID [,JIRA-ID] +for example: + ``./examples/sonar-scan-example/bin/sonar-scan-extractor-linux-amd64 --reportTaskFile=$PWD/.scannerwork/report-task.txt --FailOnAnalysisFailure > predicate.json +`` +4. call the evidence create cli with the predicate.json file +for example: +`` +jf evd create \ + --build-name $GITHUB_WORKFLOW \ + --build-number "${{ github.run_number }}" \ + --predicate ./predicate.json \ + --predicate-type https://jfrog.com/evidence/sonar-scan/v1 \ + --key "${{ secrets.JIRA_TEST_PKEY }}" \ + --key-alias ${{ vars.JIRA_TEST_KEY }} +`` + +## Additional considerations +```plaintext +It is advised to decide if to create an evidence with sonar analysis failure scan or refrain from creating the evidence. +to create the evidence with an analysis gateway failure content, do **not** add the `--FailOnAnalysisFailure` argument. + +to refrain from creating the evidence with an analysis gateway failure content, add the `--FailOnAnalysisFailure` argument. +then check the exit code of the script and decide if to create the evidence or not. +``` \ No newline at end of file diff --git a/examples/sonar-scan-example/build-binary.sh b/examples/sonar-scan-example/build-binary.sh new file mode 100755 index 0000000..63a8675 --- /dev/null +++ b/examples/sonar-scan-example/build-binary.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Script inspired by https://www.digitalocean.com/community/tutorials/how-to-build-go-executables-for-multiple-platforms-on-ubuntu-16-04 + +errorExit () { + echo; echo "ERROR: $1"; echo + exit 1 +} + +BIN=sonar-scan-extractor +rm -rf bin +mkdir -p bin + +echo "Building $BIN" +#platforms=("darwin/amd64" "linux/arm64" "linux/amd64" "windows/amd64" "windows/386") +platforms=("linux/arm64" "linux/amd64" "darwin/arm64" ) + +for p in "${platforms[@]}"; do + platform_array=(${p//\// }) + GOOS=${platform_array[0]} + GOARCH=${platform_array[1]} + + echo -e "\nBuilding" + echo "OS: $GOOS" + echo "ARCH: $GOARCH" + final_name=$BIN'-'$GOOS'-'$GOARCH + if [ "$GOOS" = "windows" ]; then + final_name+='.exe' + fi + + env GOOS="$GOOS" GOARCH="$GOARCH" go build -o bin/$final_name . || errorExit "Building $final_name failed" +done + +echo -e "\nDone!\nThe following binaries were created in the bin/ directory:" +ls -1 bin/ +echo \ No newline at end of file diff --git a/examples/sonar-scan-example/main.go b/examples/sonar-scan-example/main.go new file mode 100644 index 0000000..cc26d14 --- /dev/null +++ b/examples/sonar-scan-example/main.go @@ -0,0 +1,294 @@ +package main +import ( + "context" + "encoding/json" + "os" + "fmt" + "io" + "log" + "net/http" + "time" + "bufio" + "strings" + "strconv" + ) + /* + SonarScanResponse is the json analysis that was outputed by sonar + and will be returned to the calling build process for cresting an evidence + + notice that the calling client should first check that return value was 0 before using the response JSON, + otherwise the response is an error message which cannot be parsed + */ +// sonar response struct +type SonarResponse struct { + Task SonarTask `json:"task"` + Analysis ProjectStatus `json:"analysis"` +} + +// Define a struct to hold the response data +type SonarTaskResponse struct { + Task SonarTask `json:"task"` +} + +type SonarTask struct { + id string `json:"id"` + Status string `json:"status"` + AnalysisId string `json:"analysisId"` + ComponentId string `json:"componentId"` + ComponentKey string `json:"componentKey"` + ComponentName string `json:"componentName"` + Organization string `json:"organization"` + SubmittedAt string `json:"submittedAt"` + SubmitterLogin string `json:"submitterLogin"` + StartedAt string `json:"startedAt"` + ExecutedAt string `json:"executedAt"` + } +type Condition struct { + Status string `json:"status"` + MetricKey string `json:"metricKey"` + Comparator string `json:"comparator"` + PeriodIndex int `json:"periodIndex"` + ErrorThreshold string `json:"errorThreshold"` + ActualValue string `json:"actualValue"` +} + +type Period struct { + Index int `json:"index"` + Mode string `json:"mode"` + Date string `json:"date"` +} + +type ProjectStatus struct { + Status string `json:"status"` + Conditions []Condition `json:"conditions"` + Periods []Period `json:"periods"` + IgnoredConditions bool `json:"ignoredConditions"` +} + +type SonarAnalysis struct { + ProjectStatus ProjectStatus `json:"projectStatus"` +} + +const ( + DEFAULT_HTTP_TIMEOUT = 10 * time.Second + ANALYSIS_URL = "https://sonarcloud.io/api/qualitygates/project_status?analysisId=$analysisId" + LOG_FILE_LOCATION = "sonar-scan.log" +) +func main() { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + logFile, err := os.OpenFile(LOG_FILE_LOCATION, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatal(err) + } + defer logFile.Close() + logger := log.New(logFile, "[SONAR EVIDENCE CREATION] ", log.Ldate|log.Ltime|log.Lshortfile) + logger.Println("Running sonar analysis extraction") + sonar_token := os.Getenv("SONAR_TOKEN") + if sonar_token == "" { + logger.Println("Sonar token not found, set SONAR_TOKEN variable") + os.Exit(1) + } +///home/runner/work/Evidence-Examples/Evidence-Examples/.scannerwork/report-task.txt + //get the sonar report file location or details to .scannerwork/.report-task.txt + reportTaskFile := ".scannerwork/.report-task.txt" + failOnAnalysisFailure := false + maxRetries := 0 + waitTime := 5 + if len(os.Args) > 0 { + // loop over all args + for i, arg := range os.Args { + if i == 0 { + continue + } + if strings.HasPrefix(arg, "--reportTaskFile=") { + reportTaskFile = strings.TrimPrefix(arg, "--reportTaskFile=") + } else if strings.HasPrefix(arg, "--FailOnAnalysisFailure") { + failOnAnalysisFailure = true + } else if strings.HasPrefix(arg, "--MaxRetries=") { + maxRetriesStr := strings.TrimPrefix(arg, "--MaxRetries=") + maxRetries, err = strconv.Atoi(maxRetriesStr) + if err != nil { + logger.Println("Invalid wait time argument:",maxRetriesStr , "error:" ,err) + os.Exit(1) + } + } else if strings.HasPrefix(arg, "--WaitTime=") { + waitTimeStr := strings.TrimPrefix(arg, "--WaitTime=") + waitTime, err = strconv.Atoi(waitTimeStr) + if err != nil { + logger.Println("Invalid wait time argument:",waitTimeStr , "error:" ,err) + os.Exit(1) + } + } + } + logger.Println("reportTaskFile:", reportTaskFile) + logger.Println("FailOnAnalysisFailure:", failOnAnalysisFailure) + logger.Println("maxRetries:", maxRetries) + logger.Println("WaitTime:", waitTime) + } + // fmt.Println("reportTaskFile: ", reportTaskFile) + // Open the reportTaskFile + file, err := os.Open(reportTaskFile) + if err != nil { + logger.Println("Error opening file:", reportTaskFile, "error:", err) + os.Exit(1) + } + defer file.Close() + ceTaskUrl:="" + + // Read the file line by line + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + // Skip empty lines and comments + if len(line) == 0 || strings.HasPrefix(line, "#") { + continue + } + // Split the line into key and value + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + if key == "ceTaskUrl" { + ceTaskUrl = value + break + } + } + } + if err := scanner.Err(); err != nil { + logger.Println("Error reading file:", reportTaskFile , "error", err) + os.Exit(1) + } + if ceTaskUrl == "" { + fmt.Printf("ceTaskUrl Key not found") + os.Exit(1) + } + // Add a reusable HTTP client + var client = &http.Client{ + Timeout: DEFAULT_HTTP_TIMEOUT, + Transport: &http.Transport{ + MaxIdleConns: 100, + IdleConnTimeout: 10 * time.Second, + DisableCompression: true, + }, + } + logger.Println("ceTaskUrl", ceTaskUrl) + // get the report task + retries := 0 + + var taskResponse SonarTaskResponse + for retries < maxRetries { + taskResponse, err = getReport(ctx, client, logger, ceTaskUrl, sonar_token ) + if err != nil { + logger.Println("Error getting sonar report task", err) + os.Exit(1) + } + if taskResponse.Task.Status == "SUCCESS" { + logger.Println("Sonar analysis task completed successfully after ", retries, " retries") + break + } + if taskResponse.Task.Status == "PENDING" || taskResponse.Task.Status == "IN_PROGRESS" { + logger.Println("Sonar analysis task is still in progress, waiting for ", waitTime, " seconds before retrying") + time.Sleep(time.Duration(waitTime) * time.Second) + retries++ + } + } + if (taskResponse.Task.Status != "SUCCESS") { + logger.Println("Sonar analysis task after ", maxRetries, " retries is ", taskResponse.Task.Status, "exiting") + os.Exit(1) + } + + logger.Println("taskResponse.Task.AnalysisId", taskResponse.Task.AnalysisId) + // get the analysis content + analysis , err := getAnalysis(ctx, client, logger, sonar_token, taskResponse.Task.AnalysisId) + if err != nil { + logger.Println("Error getting sonar analysis report: ", err) + os.Exit(1) + } + if analysis.ProjectStatus.Status != "OK" && failOnAnalysisFailure { + logger.Println("Sonar analysis failed, exiting according to failOnAnalysisFailure argument") + os.Exit(1) + } + + response := SonarResponse{ + Task: taskResponse.Task, + Analysis: analysis.ProjectStatus, + } + + // marshal the response to JSON + jsonBytes, err := json.Marshal(response) + if err != nil { + logger.Println("Error marshaling JSON", err) + os.Exit(1) + } + // return response to caller through stdout + os.Stdout.Write(jsonBytes) + } + + +func getReport(ctx context.Context , client *http.Client, logger *log.Logger, ceTaskUrl string, sonar_token string) (SonarTaskResponse, error) { + // Make the HTTP GET request + logger.Println("getReport ceTaskUrl:",ceTaskUrl) + req, err := http.NewRequestWithContext(ctx, "GET", ceTaskUrl, nil) + req.Header.Set("Authorization", "Bearer " + sonar_token) + resp, err := client.Do(req) + if err != nil { + return SonarTaskResponse{}, fmt.Errorf("Error making the request, url:",ceTaskUrl, "error", err) + } + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + logger.Println("getReport error getting response body error:", err) + return SonarTaskResponse{}, fmt.Errorf("getReport error getting response body error, url:",ceTaskUrl, "error", err) + } + + if resp.StatusCode != http.StatusOK { + return SonarTaskResponse{}, fmt.Errorf("getReport error getting response from", ceTaskUrl, " returned ", resp.StatusCode, " response body ", body) + } + + logger.Println("getReport resp.StatusCode:", resp.StatusCode) + + var taskResponse SonarTaskResponse + err = json.Unmarshal(body, &taskResponse) + if err != nil { + logger.Println("getReport error Unmarshal response body ", string(body)) + return SonarTaskResponse{}, fmt.Errorf("error unmarshal report response for report", ceTaskUrl, "error", err) + } + logger.Println("getReport taskResponse:", taskResponse) + return taskResponse, nil +} + +func getAnalysis(ctx context.Context, client *http.Client, logger *log.Logger, sonar_token string, analysisId string) (SonarAnalysis, error) { + + analysisUrl := strings.Replace(ANALYSIS_URL, "$analysisId", analysisId, 1) + logger.Println("analysisId", analysisId) + //logger.Println("analysisUrl", analysisUrl) + // Make the HTTP GET request + req, err := http.NewRequestWithContext(ctx, "GET", analysisUrl , nil) + req.Header.Set("Authorization", "Bearer " + sonar_token) + resp, err := client.Do(req) + if err != nil { + return SonarAnalysis{}, fmt.Errorf("getAnalysis, Error making the request, url:",analysisUrl, "error", err) + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + logger.Println("getAnalysis error getting response body error:", err) + return SonarAnalysis{}, fmt.Errorf("getAnalysis error getting response body error, url:",analysisUrl, "error", err) + } + + if resp.StatusCode != http.StatusOK { + return SonarAnalysis{}, fmt.Errorf("getAnalysis, error getting response from", analysisUrl, " returned ", resp.StatusCode, " response body ", body) + } + + var analysisResponse SonarAnalysis + err = json.Unmarshal(body, &analysisResponse) + if err != nil { + log.Println("getAnalysis, get temp credentials response body ", string(body)) + return SonarAnalysis{}, fmt.Errorf("getAnalysis, error unmarshal analysis response", analysisUrl, "error", err) + } + + return analysisResponse, nil +} \ No newline at end of file diff --git a/main.go b/main.go index 927f5fc..6175604 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,6 @@ package main import ( "fmt" "net/http" - "github.com/hbollon/go-edlib" ) @@ -12,7 +11,7 @@ func helloHandler(w http.ResponseWriter, r *http.Request) { } func main() { - res, err := edlib.StringsSimilarity("string1", "string2", edlib.Levenshtein) + res, err := edlib.StringsSimilarity("my name is Bob", "His name is David", edlib.Levenshtein) if err != nil { fmt.Println(err) } else { @@ -21,4 +20,5 @@ func main() { http.HandleFunc("/hello", helloHandler) http.ListenAndServe(":9001", nil) + fmt.Printf("exiting server") } diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..16b4962 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,19 @@ +# Required metadata +sonar.projectKey=test-evidence +sonar.organization=my-evidence-test-org + +# Project details +sonar.projectName=test-evidence-project +sonar.projectVersion=1.0 + +# Path to source directories +sonar.sources=. + +# Language +sonar.language=go + +# Encoding of the source files +sonar.sourceEncoding=UTF-8 + +# Go specific settings +sonar.go.coverage.reportPaths=coverage.out \ No newline at end of file