diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index fcee4882af4..6199fffc5b7 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -47,6 +47,7 @@ jobs:
env:
GOOGLE_ANALYTICS: ${{ secrets.GOOGLE_ANALYTICS }}
GOOGLE_SITE_VERIFICATION: ${{ secrets.GOOGLE_SITE_VERIFICATION }}
+ INCLUDE_OSANO: true
- name: Cleanup build
run: rm -rf attack-versions
diff --git a/attack-style/package-lock.json b/attack-style/package-lock.json
index d6e38e2ee58..ea3a162e085 100644
--- a/attack-style/package-lock.json
+++ b/attack-style/package-lock.json
@@ -478,25 +478,49 @@
"url": "https://opencollective.com/parcel"
}
},
- "node_modules/@sindresorhus/is": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
- "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
+ "node_modules/@types/cacheable-request": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+ "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
"license": "MIT",
- "engines": {
- "node": ">=6"
+ "dependencies": {
+ "@types/http-cache-semantics": "*",
+ "@types/keyv": "^3.1.4",
+ "@types/node": "*",
+ "@types/responselike": "^1.0.0"
}
},
- "node_modules/@szmarczak/http-timer": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
- "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/keyv": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+ "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
"license": "MIT",
"dependencies": {
- "defer-to-connect": "^1.0.1"
- },
- "engines": {
- "node": ">=6"
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz",
+ "integrity": "sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.8.0"
+ }
+ },
+ "node_modules/@types/responselike": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
}
},
"node_modules/a-sync-waterfall": {
@@ -728,63 +752,6 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
- "node_modules/cacheable-request": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
- "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
- "license": "MIT",
- "dependencies": {
- "clone-response": "^1.0.2",
- "get-stream": "^5.1.0",
- "http-cache-semantics": "^4.0.0",
- "keyv": "^3.0.0",
- "lowercase-keys": "^2.0.0",
- "normalize-url": "^4.1.0",
- "responselike": "^1.0.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cacheable-request/node_modules/get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "license": "MIT",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cacheable-request/node_modules/json-buffer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
- "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==",
- "license": "MIT"
- },
- "node_modules/cacheable-request/node_modules/keyv": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
- "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.0"
- }
- },
- "node_modules/cacheable-request/node_modules/lowercase-keys": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
- "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -1029,6 +996,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/clone-response/node_modules/mimic-response": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/clone-stats": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
@@ -1102,9 +1078,9 @@
"license": "MIT"
},
"node_modules/commander": {
- "version": "2.17.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
- "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
"node_modules/concat-map": {
@@ -1287,15 +1263,30 @@
}
},
"node_modules/decompress-response": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
- "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"license": "MIT",
"dependencies": {
- "mimic-response": "^1.0.0"
+ "mimic-response": "^3.1.0"
},
"engines": {
- "node": ">=4"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-extend": {
@@ -1308,10 +1299,13 @@
}
},
"node_modules/defer-to-connect": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
- "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
- "license": "MIT"
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
},
"node_modules/define-data-property": {
"version": "1.1.4",
@@ -1446,12 +1440,6 @@
"safe-buffer": "~5.1.0"
}
},
- "node_modules/duplexer3": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
- "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==",
- "license": "BSD-3-Clause"
- },
"node_modules/duplexify": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@@ -1886,18 +1874,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "license": "MIT",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -2115,28 +2091,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/got": {
- "version": "9.6.0",
- "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
- "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
- "license": "MIT",
- "dependencies": {
- "@sindresorhus/is": "^0.14.0",
- "@szmarczak/http-timer": "^1.1.2",
- "cacheable-request": "^6.0.0",
- "decompress-response": "^3.3.0",
- "duplexer3": "^0.1.4",
- "get-stream": "^4.1.0",
- "lowercase-keys": "^1.0.1",
- "mimic-response": "^1.0.1",
- "p-cancelable": "^1.0.0",
- "to-readable-stream": "^1.0.0",
- "url-parse-lax": "^3.0.0"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2207,24 +2161,24 @@
}
},
"node_modules/html-minifier": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz",
- "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
+ "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"license": "MIT",
"dependencies": {
- "camel-case": "3.0.x",
- "clean-css": "4.2.x",
- "commander": "2.17.x",
- "he": "1.2.x",
- "param-case": "2.1.x",
- "relateurl": "0.2.x",
- "uglify-js": "3.4.x"
+ "camel-case": "^3.0.0",
+ "clean-css": "^4.2.1",
+ "commander": "^2.19.0",
+ "he": "^1.2.0",
+ "param-case": "^2.1.1",
+ "relateurl": "^0.2.7",
+ "uglify-js": "^3.5.1"
},
"bin": {
"html-minifier": "cli.js"
},
"engines": {
- "node": ">=4"
+ "node": ">=6"
}
},
"node_modules/html-tags": {
@@ -2241,9 +2195,9 @@
}
},
"node_modules/http-cache-semantics": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
- "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
"license": "BSD-2-Clause"
},
"node_modules/ignore": {
@@ -2581,7 +2535,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/json-parse-even-better-errors": {
@@ -2617,7 +2570,6 @@
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
@@ -2902,15 +2854,6 @@
"integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==",
"license": "MIT"
},
- "node_modules/lowercase-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
- "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -2935,18 +2878,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/marked": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.3.tgz",
- "integrity": "sha512-Fqa7eq+UaxfMriqzYLayfqAE40WN03jf+zHjT18/uXNuzjq3TY0XTbrAoPeqSJrAmPz11VuUA+kBPYOhHt9oOQ==",
- "license": "MIT",
- "bin": {
- "marked": "bin/marked"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -3017,15 +2948,6 @@
"node": ">=8.6"
}
},
- "node_modules/mimic-response": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -3130,15 +3052,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/normalize-url": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
- "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/now-and-later": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
@@ -3246,15 +3159,6 @@
"safe-buffer": "~5.1.0"
}
},
- "node_modules/p-cancelable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
- "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/package-json": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
@@ -3270,6 +3174,152 @@
"node": ">=8"
}
},
+ "node_modules/package-json/node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/package-json/node_modules/@szmarczak/http-timer": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+ "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/package-json/node_modules/cacheable-lookup": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+ "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.6.0"
+ }
+ },
+ "node_modules/package-json/node_modules/cacheable-request": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+ "license": "MIT",
+ "dependencies": {
+ "clone-response": "^1.0.2",
+ "get-stream": "^5.1.0",
+ "http-cache-semantics": "^4.0.0",
+ "keyv": "^4.0.0",
+ "lowercase-keys": "^2.0.0",
+ "normalize-url": "^6.0.1",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/package-json/node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json/node_modules/got": {
+ "version": "11.8.6",
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^4.0.0",
+ "@szmarczak/http-timer": "^4.0.5",
+ "@types/cacheable-request": "^6.0.1",
+ "@types/responselike": "^1.0.0",
+ "cacheable-lookup": "^5.0.3",
+ "cacheable-request": "^7.0.2",
+ "decompress-response": "^6.0.0",
+ "http2-wrapper": "^1.0.0-beta.5.2",
+ "lowercase-keys": "^2.0.0",
+ "p-cancelable": "^2.0.0",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "node_modules/package-json/node_modules/http2-wrapper": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+ "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+ "license": "MIT",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "node_modules/package-json/node_modules/lowercase-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/package-json/node_modules/normalize-url": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json/node_modules/p-cancelable": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/package-json/node_modules/responselike": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
+ "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/package-json/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -3483,15 +3533,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/prepend-http": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
- "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -3573,6 +3614,18 @@
],
"license": "MIT"
},
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -3746,6 +3799,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+ "license": "MIT"
+ },
"node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
@@ -3768,15 +3827,6 @@
"node": ">= 0.10"
}
},
- "node_modules/responselike": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
- "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==",
- "license": "MIT",
- "dependencies": {
- "lowercase-keys": "^1.0.0"
- }
- },
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -3960,6 +4010,18 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
+ "node_modules/sass-convert/node_modules/semver-regex": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz",
+ "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/sass-convert/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -4023,6 +4085,18 @@
"marked": "^0.6.2"
}
},
+ "node_modules/sassdoc-extras/node_modules/marked": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
+ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/sassdoc-theme-default": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/sassdoc-theme-default/-/sassdoc-theme-default-2.8.6.tgz",
@@ -4181,15 +4255,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/semver-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz",
- "integrity": "sha512-1vZcoRC+LPtHFkLUPyrabsATDSHerxW+hJBN8h04HZOZBuewbXaNROtUVdEPrTdZsWNq6sfsXDhd48GB2xTG4g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -4677,15 +4742,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/to-readable-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
- "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -4776,14 +4832,10 @@
}
},
"node_modules/uglify-js": {
- "version": "3.4.10",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
- "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==",
+ "version": "3.19.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
+ "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
"license": "BSD-2-Clause",
- "dependencies": {
- "commander": "~2.19.0",
- "source-map": "~0.6.1"
- },
"bin": {
"uglifyjs": "bin/uglifyjs"
},
@@ -4791,12 +4843,6 @@
"node": ">=0.8.0"
}
},
- "node_modules/uglify-js/node_modules/commander": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
- "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
- "license": "MIT"
- },
"node_modules/unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@@ -4806,6 +4852,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/undici-types": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
+ "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
+ "license": "MIT"
+ },
"node_modules/unique-stream": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
@@ -4874,18 +4926,6 @@
"integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==",
"license": "MIT"
},
- "node_modules/url-parse-lax": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
- "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==",
- "license": "MIT",
- "dependencies": {
- "prepend-http": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/attack-style/package.json b/attack-style/package.json
index 0dcfc246e2b..f7c43ad7b19 100644
--- a/attack-style/package.json
+++ b/attack-style/package.json
@@ -23,5 +23,11 @@
"dependencies": {
"sass": "^1.83.0",
"sassdoc": "^2.7.4"
+ },
+ "overrides": {
+ "marked": "^4.0.10",
+ "got": "^11.8.5",
+ "semver-regex": "^3.1.4",
+ "html-minifier": "^4.0.0"
}
}
diff --git a/attack-theme/static/images/benefactors/BoA.png b/attack-theme/static/images/benefactors/BoA.png
new file mode 100644
index 00000000000..a311231ec83
Binary files /dev/null and b/attack-theme/static/images/benefactors/BoA.png differ
diff --git a/attack-theme/templates/general/base-template.html b/attack-theme/templates/general/base-template.html
index 7ca955c35de..61d60899d28 100644
--- a/attack-theme/templates/general/base-template.html
+++ b/attack-theme/templates/general/base-template.html
@@ -23,6 +23,7 @@
{% block head %}
+ {% include "general/osano.html" %}
{% include "general/google-analytics.html" %}
{% include "general/google-site-verification.html" %}
@@ -106,9 +107,12 @@
{% endif %}
-
diff --git a/attack-theme/templates/general/osano.html b/attack-theme/templates/general/osano.html
new file mode 100644
index 00000000000..4b91c1eead3
--- /dev/null
+++ b/attack-theme/templates/general/osano.html
@@ -0,0 +1,6 @@
+{% if INCLUDE_OSANO %}
+
+
+{% endif %}
diff --git a/custom_jinja_filters.py b/custom_jinja_filters.py
index 654d694ff53..83cde0013c0 100644
--- a/custom_jinja_filters.py
+++ b/custom_jinja_filters.py
@@ -71,7 +71,7 @@ def clean_stix_data(data):
def get_citations(data):
"""Given a description, find all of the citations."""
- p = re.compile("\(Citation: (.*?)\)")
+ p = re.compile(r"\(Citation: (.*?)\)")
return p.findall(data)
@@ -92,7 +92,7 @@ def get_html_citation(citations, citation_name):
if citation:
ref_number = None
description = citation.get("description")
-
+
if citation.get("number"):
ref_number = citation["number"]
else:
@@ -101,10 +101,19 @@ def get_html_citation(citations, citation_name):
citation["number"] = ref_number
if not citation.get("url"):
- reference_html = reference_marker_template_no_url.format(ref_number, ref_number, citation_name, description, ref_number)
+ reference_html = reference_marker_template_no_url.format(
+ ref_number, ref_number, citation_name, description, ref_number
+ )
else:
reference_html = reference_marker_template.format(
- ref_number, ref_number, description, citation_name, citation["url"], ref_number - 1, ref_number - 1, ref_number
+ ref_number,
+ ref_number,
+ description,
+ citation_name,
+ citation["url"],
+ ref_number - 1,
+ ref_number - 1,
+ ref_number,
)
return reference_html
@@ -158,7 +167,7 @@ def stixToHTML(data, citations, firstParagraphOnly, convert):
citations (optional, object), if not None, add citation markers to the data.
firstParagraphOnly (optional, boolean), if true, only return the first paragraph of the data in question.
"""
- if (convert):
+ if convert:
# Replace data from markdown format
data = markdown.markdown(data)
diff --git a/modules/__init__.py b/modules/__init__.py
index eda35e27112..e9636e7878a 100644
--- a/modules/__init__.py
+++ b/modules/__init__.py
@@ -14,7 +14,9 @@ def sort_menu_by_priority():
def sort_run_ptr_by_priority():
global run_ptr
run_ptr = sorted(run_ptr, key=lambda k: k["priority"])
- print(f"Building website using the following modules in this order: {[pointer_info['module_name'] for pointer_info in run_ptr]}")
+ print(
+ f"Building website using the following modules in this order: {[pointer_info['module_name'] for pointer_info in run_ptr]}"
+ )
def check_redirections(redirections_list):
diff --git a/modules/assets/__init__.py b/modules/assets/__init__.py
index f39002b53e5..64f6ab8ef60 100644
--- a/modules/assets/__init__.py
+++ b/modules/assets/__init__.py
@@ -5,5 +5,6 @@
def get_priority():
return assets_config.priority
+
def run_module():
return (assets.generate_assets(), assets_config.module_name)
diff --git a/modules/assets/assets.py b/modules/assets/assets.py
index 25c166e201e..26f45360d10 100644
--- a/modules/assets/assets.py
+++ b/modules/assets/assets.py
@@ -51,18 +51,14 @@ def generate_markdown_files():
data = {}
notes = util.relationshipgetters.get_objects_using_notes()
- side_menu_data = util.buildhelpers.get_side_menu_data(
- "Assets", "/assets/", asset_list_no_deprecated_revoked
- )
+ side_menu_data = util.buildhelpers.get_side_menu_data("Assets", "/assets/", asset_list_no_deprecated_revoked)
data["side_menu_data"] = side_menu_data
data["assets_table"] = get_assets_table_data(asset_list_no_deprecated_revoked)
data["assets_list_len"] = str(len(asset_list_no_deprecated_revoked))
subs = assets_config.asset_index_md + json.dumps(data)
- with open(
- os.path.join(assets_config.asset_markdown_path, "overview.md"), "w", encoding="utf8"
- ) as md_file:
+ with open(os.path.join(assets_config.asset_markdown_path, "overview.md"), "w", encoding="utf8") as md_file:
md_file.write(subs)
# Create the markdown for assets
@@ -77,7 +73,8 @@ def generate_asset_md(asset, side_menu_data, notes):
attack_id = util.buildhelpers.get_attack_id(asset)
- if not attack_id: return
+ if not attack_id:
+ return
data = {}
data["attack_id"] = attack_id
@@ -178,7 +175,8 @@ def get_assets_table_data(asset_list):
assets_table_data = []
for asset in asset_list:
attack_id = util.buildhelpers.get_attack_id(asset)
- if not attack_id: continue
+ if not attack_id:
+ continue
domain_list = util.buildhelpers.get_domain_name(asset)
row = {
@@ -187,7 +185,7 @@ def get_assets_table_data(asset_list):
}
for domain_idx in range(len(domain_list)):
- domain_list[domain_idx] = domain_list[domain_idx].replace('-attack','')
+ domain_list[domain_idx] = domain_list[domain_idx].replace("-attack", "")
if domain_list[domain_idx] == "ics":
domain_list[domain_idx] = domain_list[domain_idx].upper()
else:
@@ -206,12 +204,13 @@ def get_assets_table_data(asset_list):
def get_related_asset_data(related_assets):
- if not related_assets: return []
+ if not related_assets:
+ return []
related_asset_data = []
for related_asset in related_assets:
row = {
- "name": related_asset["name"], # required
+ "name": related_asset["name"], # required
}
if related_asset.get("related_asset_sectors"):
related_asset["related_asset_sectors"].sort()
@@ -224,7 +223,7 @@ def get_related_asset_data(related_assets):
def get_techniques_targeting_asset_data(asset, reference_list):
"""Given an asset and its reference list, get the techniques targeting the asset.
- Check the reference list for citations, if not found in list, add it.
+ Check the reference list for citations, if not found in list, add it.
"""
technique_list = {}
techniques_targeting_assets = util.relationshipgetters.get_techniques_targeting_assets()
diff --git a/modules/assets/assets_config.py b/modules/assets/assets_config.py
index deafdb527d0..49c583c4a21 100644
--- a/modules/assets/assets_config.py
+++ b/modules/assets/assets_config.py
@@ -7,14 +7,10 @@
asset_markdown_path = "content/pages/assets/"
# String template for asset index page
-asset_index_md = (
- "Title: Asset overview\nTemplate: assets/assets-index\nsave_as: assets/index.html\ndata: "
-)
+asset_index_md = "Title: Asset overview\nTemplate: assets/assets-index\nsave_as: assets/index.html\ndata: "
# String template for asset page
-asset_md = Template(
- "Title: ${name}\nTemplate: assets/asset\nsave_as: assets/${attack_id}/index.html\ndata: "
-)
+asset_md = Template("Title: ${name}\nTemplate: assets/asset\nsave_as: assets/${attack_id}/index.html\ndata: ")
# Path for templates
assets_templates_path = "modules/assets/templates/"
diff --git a/modules/benefactors/__init__.py b/modules/benefactors/__init__.py
index 68eb55f3e77..8fd0ea8cb93 100644
--- a/modules/benefactors/__init__.py
+++ b/modules/benefactors/__init__.py
@@ -17,5 +17,6 @@ def get_menu():
"children": [],
}
+
def run_module():
return (benefactors.generate_benefactors(), benefactors_config.module_name)
diff --git a/modules/benefactors/benefactors.py b/modules/benefactors/benefactors.py
index 4861ab2fdbe..ac6045a0221 100644
--- a/modules/benefactors/benefactors.py
+++ b/modules/benefactors/benefactors.py
@@ -24,5 +24,7 @@ def generate_benefactors():
benefactors_md = benefactors_config.benefactors_md
# write markdown to file
- with open(os.path.join(benefactors_config.benefactors_markdown_path, "benefactors.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(benefactors_config.benefactors_markdown_path, "benefactors.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(benefactors_md)
diff --git a/modules/benefactors/benefactors_config.py b/modules/benefactors/benefactors_config.py
index 29ef1259880..fac102ce451 100644
--- a/modules/benefactors/benefactors_config.py
+++ b/modules/benefactors/benefactors_config.py
@@ -6,7 +6,10 @@
# String template for benefactors index page
benefactors_md = (
- "Title: Benefactors\n" "Template: benefactors/benefactors\n" "save_as: resources/engage-with-attack/benefactors/index.html\n" "data: "
+ "Title: Benefactors\n"
+ "Template: benefactors/benefactors\n"
+ "save_as: resources/engage-with-attack/benefactors/index.html\n"
+ "data: "
)
benefactors_templates_path = "modules/benefactors/templates"
diff --git a/modules/benefactors/templates/benefactors.html b/modules/benefactors/templates/benefactors.html
index f34d0a81c11..f2e1cbf5a48 100644
--- a/modules/benefactors/templates/benefactors.html
+++ b/modules/benefactors/templates/benefactors.html
@@ -104,6 +104,11 @@ Supporters
logo: "/theme/images/benefactors/AttackIQ-logo.png",
url: "https://attackiq.com/",
title: "AttackIQ, Inc."
+ },
+ {
+ logo: "/theme/images/benefactors/BoA.png",
+ url: "https://www.bankofamerica.com/",
+ title: "Bank of America, N.A."
},
{
logo: "/theme/images/benefactors/citi.png",
@@ -114,16 +119,16 @@ Supporters
logo: "/theme/images/benefactors/crowdstrike.png",
url: "https://www.crowdstrike.com/",
title: "CrowdStrike, Inc."
- },
+ }
+ ],
+ },
+ {
+ sponsors: [
{
logo: "/theme/images/benefactors/fortinet.png",
url: "https://www.fortinet.com/",
title: "Fortinet"
},
- ],
- },
- {
- sponsors: [
{
logo: "/theme/images/benefactors/hca.png",
url: "https://hcahealthcare.com/",
@@ -139,16 +144,15 @@ Supporters
url: "https://www.lloydsbankinggroup.com/",
title: "Lloyds Banking Group"
},
+ ],
+ },
+ {
+ sponsors: [
{
logo: "/theme/images/benefactors//microsoft.png",
url: "https://www.microsoft.com/en-us/",
title: "Microsoft Corporation"
},
-
- ],
- },
- {
- sponsors: [
{
logo: "/theme/images/benefactors/verizon.png",
url: "https://www.verizon.com/business/",
diff --git a/modules/campaigns/__init__.py b/modules/campaigns/__init__.py
index 99ff4eaa234..cf52cc31ba6 100644
--- a/modules/campaigns/__init__.py
+++ b/modules/campaigns/__init__.py
@@ -5,5 +5,6 @@
def get_priority():
return campaigns_config.priority
+
def run_module():
return (campaigns.generate_campaigns(), campaigns_config.module_name)
diff --git a/modules/campaigns/campaigns.py b/modules/campaigns/campaigns.py
index eafbbadfcfd..4e61f930bf0 100644
--- a/modules/campaigns/campaigns.py
+++ b/modules/campaigns/campaigns.py
@@ -306,6 +306,7 @@ def get_software_table_data(campaign, reference_list):
software_data = sorted(software_data, key=lambda k: k["name"].lower())
return software_data
+
def generate_sidebar_campaigns(side_menu_data):
"""Responsible for generating the sidebar for the campaigns pages."""
logger.info("Generating campaigns sidebar")
@@ -316,5 +317,7 @@ def generate_sidebar_campaigns(side_menu_data):
sidebar_campaigns_md = campaigns_config.sidebar_campaigns_md + json.dumps(data)
# write markdown to file
- with open(os.path.join(campaigns_config.campaign_markdown_path, "sidebar_campaigns.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(campaigns_config.campaign_markdown_path, "sidebar_campaigns.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(sidebar_campaigns_md)
diff --git a/modules/campaigns/campaigns_config.py b/modules/campaigns/campaigns_config.py
index 3eae07cc697..5d908dd6400 100644
--- a/modules/campaigns/campaigns_config.py
+++ b/modules/campaigns/campaigns_config.py
@@ -8,12 +8,12 @@
# String template for campaign index page
campaign_index_md = (
- "Title: Campaign overview\n" "Template: campaigns/campaigns-index\n" "save_as: campaigns/index.html\n" "data: "
+ "Title: Campaign overview\nTemplate: campaigns/campaigns-index\nsave_as: campaigns/index.html\ndata: "
)
# String template for campaign page
campaign_md = Template(
- "Title: ${name}\n" "Template: campaigns/campaign\n" "save_as: campaigns/${attack_id}/index.html\n" "data: "
+ "Title: ${name}\nTemplate: campaigns/campaign\nsave_as: campaigns/${attack_id}/index.html\ndata: "
)
# Path for templates
@@ -26,4 +26,4 @@
"Template: general/sidebar-template \n"
"save_as: campaigns/sidebar-campaigns/index.html\n"
"data: "
-)
\ No newline at end of file
+)
diff --git a/modules/datasources/__init__.py b/modules/datasources/__init__.py
index 56b61bcbdd9..8d40ac5f5ab 100644
--- a/modules/datasources/__init__.py
+++ b/modules/datasources/__init__.py
@@ -16,13 +16,23 @@ def get_menu():
"priority": datasources_config.priority,
"children": [
{"display_name": "Data Sources", "url": "/datasources", "external_link": False, "children": []},
- {"display_name": "Mitigations", "url": "/mitigations/", "external_link": False, "children": [
- {"display_name": "Enterprise", "url": "/mitigations/enterprise/", "external_link": False, "children": []},
- {"display_name": "Mobile", "url": "/mitigations/mobile/", "external_link": False, "children": []},
- {"display_name": "ICS", "url": "/mitigations/ics/", "external_link": False, "children": []},
- ]},
- {"display_name": "Assets", "url": "/assets", "external_link": False, "children": [] }
- ]
+ {
+ "display_name": "Mitigations",
+ "url": "/mitigations/",
+ "external_link": False,
+ "children": [
+ {
+ "display_name": "Enterprise",
+ "url": "/mitigations/enterprise/",
+ "external_link": False,
+ "children": [],
+ },
+ {"display_name": "Mobile", "url": "/mitigations/mobile/", "external_link": False, "children": []},
+ {"display_name": "ICS", "url": "/mitigations/ics/", "external_link": False, "children": []},
+ ],
+ },
+ {"display_name": "Assets", "url": "/assets", "external_link": False, "children": []},
+ ],
}
diff --git a/modules/datasources/datasources.py b/modules/datasources/datasources.py
index 663a2a41f80..20f67914b74 100644
--- a/modules/datasources/datasources.py
+++ b/modules/datasources/datasources.py
@@ -229,7 +229,7 @@ def get_datasources_table_data(datasource_list):
row["id"] = attack_id
for domain_idx in range(len(domain_list)):
- domain_list[domain_idx] = domain_list[domain_idx].replace('-attack','')
+ domain_list[domain_idx] = domain_list[domain_idx].replace("-attack", "")
if domain_list[domain_idx] == "ics":
domain_list[domain_idx] = domain_list[domain_idx].upper()
else:
@@ -322,21 +322,22 @@ def get_datacomponents_data(datasource, reference_list):
def get_domains_of_datacomponent(datacomponent, techniques_detected_by_datacomponent, technique_to_domain):
- """Retrives domains of given data component"""
- domains_of_datacomponent = []
+ """Retrives domains of given data component"""
+ domains_of_datacomponent = []
- # get data components to techniques mapping to find domains
- techniques_of_datacomp = techniques_detected_by_datacomponent.get(datacomponent["id"])
- if techniques_of_datacomp:
- technique_list = {}
- for technique_rel in techniques_of_datacomp:
- attack_id = util.buildhelpers.get_attack_id(technique_rel["object"])
- if attack_id:
- domain = technique_to_domain[attack_id].split("-")[0]
- if not domain in domains_of_datacomponent:
- domains_of_datacomponent.append(domain)
+ # get data components to techniques mapping to find domains
+ techniques_of_datacomp = techniques_detected_by_datacomponent.get(datacomponent["id"])
+ if techniques_of_datacomp:
+ technique_list = {}
+ for technique_rel in techniques_of_datacomp:
+ attack_id = util.buildhelpers.get_attack_id(technique_rel["object"])
+ if attack_id:
+ domain = technique_to_domain[attack_id].split("-")[0]
+ if not domain in domains_of_datacomponent:
+ domains_of_datacomponent.append(domain)
+
+ return domains_of_datacomponent
- return domains_of_datacomponent
def get_datasources_domain(datasource):
"""Responsible for generating the list of domains for the datasources and datacomponents."""
@@ -357,10 +358,12 @@ def get_datasources_domain(datasource):
# get data component detections
techniques_of_datacomp = techniques_detected_by_datacomponent.get(datacomponent["id"])
if techniques_of_datacomp:
- domains_of_datacomponent = get_domains_of_datacomponent(datacomponent, techniques_detected_by_datacomponent, technique_to_domain)
+ domains_of_datacomponent = get_domains_of_datacomponent(
+ datacomponent, techniques_detected_by_datacomponent, technique_to_domain
+ )
# Add missing domains to data source
for domain in domains_of_datacomponent:
if not domain in domains_of_datasource:
domains_of_datasource.append(domain)
- return domains_of_datasource
\ No newline at end of file
+ return domains_of_datasource
diff --git a/modules/datasources/datasources_config.py b/modules/datasources/datasources_config.py
index 584af6d1eff..3d062782e77 100644
--- a/modules/datasources/datasources_config.py
+++ b/modules/datasources/datasources_config.py
@@ -11,16 +11,13 @@
# String template for data sources index page
datasource_index_md = (
- "Title: Data Sources overview\n"
- "Template: datasources/datasources-index\n"
- "save_as: datasources/index.html\n"
- "data: "
+ "Title: Data Sources overview\nTemplate: datasources/datasources-index\nsave_as: datasources/index.html\ndata: "
)
# String template for data source page
datasource_md = Template(
- "Title: ${name}\n" "Template: datasources/datasource\n" "save_as: datasources/${attack_id}/index.html\n" "data: "
+ "Title: ${name}\nTemplate: datasources/datasource\nsave_as: datasources/${attack_id}/index.html\ndata: "
)
# Path for templates
-datasources_templates_path = "modules/datasources/templates/"
\ No newline at end of file
+datasources_templates_path = "modules/datasources/templates/"
diff --git a/modules/groups/__init__.py b/modules/groups/__init__.py
index abe5567a490..608f6037250 100644
--- a/modules/groups/__init__.py
+++ b/modules/groups/__init__.py
@@ -6,6 +6,7 @@
def get_priority():
return groups_config.priority
+
def get_menu():
return {
"display_name": groups_config.module_tab_name,
@@ -17,8 +18,9 @@ def get_menu():
{"display_name": "Groups", "url": "/groups", "external_link": False, "children": []},
{"display_name": "Software", "url": "/software", "external_link": False, "children": []},
{"display_name": "Campaigns", "url": "/campaigns", "external_link": False, "children": []},
- ]
+ ],
}
+
def run_module():
return (groups.generate_groups(), groups_config.module_name)
diff --git a/modules/groups/groups_config.py b/modules/groups/groups_config.py
index 20a7da88a4e..a096ebf0367 100644
--- a/modules/groups/groups_config.py
+++ b/modules/groups/groups_config.py
@@ -8,10 +8,10 @@
group_markdown_path = "content/pages/groups/"
# String template for group index page
-group_index_md = "Title: Group overview\n" "Template: groups/groups-index\n" "save_as: groups/index.html\n" "data: "
+group_index_md = "Title: Group overview\nTemplate: groups/groups-index\nsave_as: groups/index.html\ndata: "
# String template for group page
-group_md = Template("Title: ${name}\n" "Template: groups/group\n" "save_as: groups/${attack_id}/index.html\n" "data: ")
+group_md = Template("Title: ${name}\nTemplate: groups/group\nsave_as: groups/${attack_id}/index.html\ndata: ")
# Path for templates
groups_templates_path = "modules/groups/templates/"
@@ -19,8 +19,5 @@
groups_redirection_location = "modules/groups/groups_redirections.json"
sidebar_groups_md = (
- "Title: Groups Sidebar\n"
- "Template: general/sidebar-template \n"
- "save_as: groups/sidebar-groups/index.html\n"
- "data: "
-)
\ No newline at end of file
+ "Title: Groups Sidebar\nTemplate: general/sidebar-template \nsave_as: groups/sidebar-groups/index.html\ndata: "
+)
diff --git a/modules/matrices/matrices.py b/modules/matrices/matrices.py
index 22a3ab254bf..741f17671a7 100644
--- a/modules/matrices/matrices.py
+++ b/modules/matrices/matrices.py
@@ -260,6 +260,7 @@ def transform_tactic(tactic_id):
return data, has_subtechniques, tour_technique
+
def generate_sidebar_matrices(side_menu_data):
"""Responsible for generating the sidebar for the matrices pages."""
logger.info("Generating matrices sidebar")
@@ -270,5 +271,7 @@ def generate_sidebar_matrices(side_menu_data):
sidebar_matrices_md = matrices_config.sidebar_matrices_md + json.dumps(data)
# write markdown to file
- with open(os.path.join(matrices_config.matrix_markdown_path, "sidebar_matrices.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(matrices_config.matrix_markdown_path, "sidebar_matrices.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(sidebar_matrices_md)
diff --git a/modules/matrices/matrices_config.py b/modules/matrices/matrices_config.py
index be228ef91bd..0a0408519e4 100644
--- a/modules/matrices/matrices_config.py
+++ b/modules/matrices/matrices_config.py
@@ -18,9 +18,7 @@
)
# String template for main domain matrices
-matrix_md = Template(
- "Title: Matrix-${domain}\n" "Template: matrices/matrix\n" "save_as: matrices/${path}/index.html\n" "data: "
-)
+matrix_md = Template("Title: Matrix-${domain}\nTemplate: matrices/matrix\nsave_as: matrices/${path}/index.html\ndata: ")
# String template for platform matrices
platform_md = Template(
diff --git a/modules/mitigations/mitigations.py b/modules/mitigations/mitigations.py
index e5bab98ef11..f89e2ace629 100644
--- a/modules/mitigations/mitigations.py
+++ b/modules/mitigations/mitigations.py
@@ -229,6 +229,7 @@ def get_techniques_addressed_data(mitigation, reference_list):
)
return technique_data
+
def generate_sidebar_mitigations(side_nav_data):
"""Responsible for generating the sidebar for the mitigations pages."""
logger.info("Generating mitigations sidebar")
@@ -239,5 +240,7 @@ def generate_sidebar_mitigations(side_nav_data):
sidebar_mitigations_md = mitigations_config.sidebar_mitigations_md + json.dumps(data)
# write markdown to file
- with open(os.path.join(mitigations_config.mitigation_markdown_path, "sidebar_mitigations.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(mitigations_config.mitigation_markdown_path, "sidebar_mitigations.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(sidebar_mitigations_md)
diff --git a/modules/mitigations/mitigations_config.py b/modules/mitigations/mitigations_config.py
index f60ea69f4b4..1ba778f6bd4 100644
--- a/modules/mitigations/mitigations_config.py
+++ b/modules/mitigations/mitigations_config.py
@@ -27,10 +27,7 @@
# String template for all mitigations
mitigation_md = Template(
- "Title: ${name}-${domain}\n"
- "Template: mitigations/mitigation\n"
- "save_as: mitigations/${attack_id}/index.html\n"
- "data: "
+ "Title: ${name}-${domain}\nTemplate: mitigations/mitigation\nsave_as: mitigations/${attack_id}/index.html\ndata: "
)
sidebar_mitigations_md = (
@@ -38,4 +35,4 @@
"Template: general/sidebar-template \n"
"save_as: mitigations/sidebar-mitigations/index.html\n"
"data: "
-)
\ No newline at end of file
+)
diff --git a/modules/random_page/random_page.py b/modules/random_page/random_page.py
index 604e7e65e17..09e73557439 100644
--- a/modules/random_page/random_page.py
+++ b/modules/random_page/random_page.py
@@ -16,7 +16,7 @@ def generate_json():
"groups": "Group",
"software": "Software",
"campaigns": "Campaign",
- "assets": "Asset"
+ "assets": "Asset",
}
routes = {}
diff --git a/modules/resources/__init__.py b/modules/resources/__init__.py
index e1bd244cf5e..33477ec9925 100644
--- a/modules/resources/__init__.py
+++ b/modules/resources/__init__.py
@@ -27,12 +27,7 @@ def get_menu():
"external_link": False,
"children": [],
},
- {
- "display_name": "ATT&CKcon",
- "url": "/resources/attackcon/",
- "external_link": False,
- "children": []
- },
+ {"display_name": "ATT&CKcon", "url": "/resources/attackcon/", "external_link": False, "children": []},
{
"display_name": "ATT&CK Data & Tools",
"url": "/resources/attack-data-and-tools/",
@@ -63,7 +58,7 @@ def get_menu():
"url": "/resources/legal-and-branding/",
"external_link": False,
"children": [],
- }
+ },
],
}
diff --git a/modules/resources/resources.py b/modules/resources/resources.py
index f79d78488c3..c287dbcbd97 100644
--- a/modules/resources/resources.py
+++ b/modules/resources/resources.py
@@ -15,6 +15,7 @@
import urllib.parse
+
def generate_resources():
"""Responsible for generating the resources pages."""
logger.info("Generating Resources")
@@ -62,6 +63,7 @@ def generate_resources():
generate_use_case_page()
generate_sidebar_resources()
+
def copy_docs(module_docs_path):
"""Move module specific docs into the website's content directory for pelican."""
logger.info("Copying files to docs directory")
@@ -79,17 +81,20 @@ def copy_docs(module_docs_path):
else:
shutil.copyfile(os.path.join(module_docs_path, doc), os.path.join(site_config.docs_dir, doc))
+
def extract_video_id(url):
- if '=' in url and '&' in url:
- start = url.find('=') + 1
- end = url.find('&', start)
+ if "=" in url and "&" in url:
+ start = url.find("=") + 1
+ end = url.find("&", start)
return url[start:end]
return url
+
def extract_playlist_id(url):
parsed_url = urllib.parse.urlparse(url)
query_params = urllib.parse.parse_qs(parsed_url.query)
- return query_params.get('list', [None])[0]
+ return query_params.get("list", [None])[0]
+
def generate_training_pages():
"""Responsible for generating the markdown pages of the training pages."""
@@ -104,13 +109,22 @@ def generate_training_pages():
if "lessons" in trainings[training_main][module]:
if trainings[training_main][module]["is_youtube"]:
# if videos are in a playlist
- if "playlist" in trainings[training_main][module] and trainings[training_main][module]["playlist"] == True:
- trainings[training_main][module]["first_video_id"] = extract_video_id(trainings[training_main][module]["lessons"][0]["youtube"])
- trainings[training_main][module]["playlist_id"] = extract_playlist_id(trainings[training_main][module]["lessons"][0]["youtube"])
+ if (
+ "playlist" in trainings[training_main][module]
+ and trainings[training_main][module]["playlist"] == True
+ ):
+ trainings[training_main][module]["first_video_id"] = extract_video_id(
+ trainings[training_main][module]["lessons"][0]["youtube"]
+ )
+ trainings[training_main][module]["playlist_id"] = extract_playlist_id(
+ trainings[training_main][module]["lessons"][0]["youtube"]
+ )
# if videos are not in a playlist
else:
for lesson_idx in range(len(trainings[training_main][module]["lessons"])):
- trainings[training_main][module]["lessons"][lesson_idx]["video_id"] = extract_video_id(trainings[training_main][module]["lessons"][lesson_idx]["youtube"])
+ trainings[training_main][module]["lessons"][lesson_idx]["video_id"] = extract_video_id(
+ trainings[training_main][module]["lessons"][lesson_idx]["youtube"]
+ )
# Define a dictionary of training pages and their corresponding markdown templates
training_pages = {
@@ -128,9 +142,12 @@ def generate_training_pages():
# Generate markdown for each training page and write it to a file
for page_name, page_template in training_pages.items():
page_content = page_template + json.dumps(trainings)
- with open(os.path.join(site_config.resources_markdown_path, f"{page_name}.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(site_config.resources_markdown_path, f"{page_name}.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(page_content)
+
def generate_brand_page():
"""Responsible for generating the markdown pages of the training pages."""
logger.info("Generating brand")
@@ -212,6 +229,7 @@ def generate_faq_page():
with open(os.path.join(site_config.resources_markdown_path, "faq.md"), "w", encoding="utf8") as md_file:
md_file.write(faq_content)
+
def generate_static_pages():
"""Reads markdown files from the static pages directory and copies them into the markdown directory."""
logger.info("Generating static pages")
@@ -219,7 +237,7 @@ def generate_static_pages():
if not [key["module_name"] for key in modules.run_ptr if key["module_name"] == "versions"]:
util.buildhelpers.remove_element_from_sub_menu(resources_config.module_name, "Version History")
-
+
# Below code used to get a list of all updates children
updates_dict_list = {}
updates_name = []
@@ -289,7 +307,7 @@ def generate_working_with_attack():
"techniques",
"datasources",
"campaigns",
- "assets"
+ "assets",
]
# Verify if directories exists
@@ -342,7 +360,9 @@ def generate_sidebar_resources():
sidebar_resources_md = resources_config.sidebar_resources_md
# write markdown to file
- with open(os.path.join(site_config.resources_markdown_path, "sidebar_resources.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(site_config.resources_markdown_path, "sidebar_resources.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(sidebar_resources_md)
@@ -360,7 +380,7 @@ def generate_contribute_page():
data = {}
- data["contributors"] = []
+ data["contributors"] = []
contributors_first_col = []
contributors_second_col = []
@@ -384,11 +404,10 @@ def generate_contribute_page():
subs = resources_config.contribute_md + json.dumps(data)
# Open markdown file for the contribute page
- with open(
- os.path.join(site_config.resources_markdown_path, "contribute.md"), "w", encoding="utf8"
- ) as md_file:
+ with open(os.path.join(site_config.resources_markdown_path, "contribute.md"), "w", encoding="utf8") as md_file:
md_file.write(subs)
+
def generate_presentation_archive():
"""Responsible for compiling resources json into resources markdown files for rendering on the HMTL."""
logger.info("Generating presentation archive")
@@ -408,6 +427,7 @@ def generate_presentation_archive():
) as md_file:
md_file.write(resources_content)
+
def generate_use_case_page():
"""Responsible for compiling use cases json into use cases markdown file for rendering on the HMTL."""
logger.info("Generating get started pages")
@@ -424,7 +444,7 @@ def generate_use_case_page():
for i in range(len(use_case_data)):
use_case_name.append(use_case_data[i]["title"])
title = "Title: " + use_case_data[i]["title"] + "\n"
- name = use_case_data[i]["title"].lower().replace(' ','-').replace("&", "a")
+ name = use_case_data[i]["title"].lower().replace(" ", "-").replace("&", "a")
template = "Template: resources/use-cases\n"
use_case_path.append("/resources/get-started/" + name + "/")
save_as = "save_as: resources/get-started/" + name + "/index.html\n"
@@ -439,6 +459,6 @@ def generate_use_case_page():
use_case_list = use_case_dict_list["use_case_md"]
for i in range(len(use_case_list)):
use_case_content = use_case_list[i] + json.dumps(use_case_data[i])
- f_name = "use-case-" + use_case_data[i]["title"].lower().replace(' ','-') + ".md"
+ f_name = "use-case-" + use_case_data[i]["title"].lower().replace(" ", "-") + ".md"
with open(os.path.join(site_config.resources_markdown_path, f_name), "w", encoding="utf8") as md_file:
- md_file.write(use_case_content)
\ No newline at end of file
+ md_file.write(use_case_content)
diff --git a/modules/resources/resources_config.py b/modules/resources/resources_config.py
index 8a46608b937..18ef48c3d9c 100644
--- a/modules/resources/resources_config.py
+++ b/modules/resources/resources_config.py
@@ -76,12 +76,7 @@
)
# FAQ md
-faq_md = (
- "Title: Frequently Asked Questions\n"
- "Template: general/faq-overview\n"
- "save_as: resources/faq/index.html\n"
- "data: "
-)
+faq_md = "Title: Frequently Asked Questions\nTemplate: general/faq-overview\nsave_as: resources/faq/index.html\ndata: "
# Contribute md
contribute_md = (
@@ -119,7 +114,5 @@
# Sidebar md
sidebar_resources_md = (
- "Title: Resources Sidebar\n"
- "Template: general/sidebar-resources \n"
- "save_as: resources/sidebar-resources/index.html\n"
+ "Title: Resources Sidebar\nTemplate: general/sidebar-resources \nsave_as: resources/sidebar-resources/index.html\n"
)
diff --git a/modules/resources/static_pages/privacy-policy.md b/modules/resources/static_pages/privacy-policy.md
index 762cede8ce6..ad70bf9676c 100644
--- a/modules/resources/static_pages/privacy-policy.md
+++ b/modules/resources/static_pages/privacy-policy.md
@@ -6,42 +6,424 @@ Authors: Adam Pennington
url: /resources/legal-and-branding/policy
save_as: resources/legal-and-branding/privacy/index.html
-**Effective Date: 1 July 2021**
+**THE MITRE CORPORATION RESPECTS THE PRIVACY OF ITS WEBSITE USERS.**
-The MITRE Corporation ("MITRE") respects the privacy of its website users.
+**Effective Date: 10 June 2025**
-This Privacy Policy explains the types of information collected by MITRE ATT&CK® from website visitors ("User") or that you provide to MITRE through other means, such as via e-mail and web forms (collectively "Site"), and how MITRE uses, shares, protects, and retains that information. By visiting the Site, you understand and agree to terms outlined in this Privacy Policy.
+This Privacy Policy explains the types of personal information that The
+MITRE Corporation ("**MITRE** ," "**we**," "**our**," "**us**") collects
+from visitors to the MITRE ATT&CK® website (the "**Site**") at
+https://attack.mitre.org; how MITRE uses, shares, protects, stores, and
+otherwise processes that personal information; and your choices with
+respect to our use of your personal information. By using our Site, you
+acknowledge that you understand and agree to the terms outlined in this
+Privacy Policy. If you have any questions, you may contact us using the
+information provided at the end of this Privacy Policy.
-## Information Collected from Cookies on Web Traffic Reporting and Content Tools
+This notice is provided in a layered format so you can click through to
+the specific areas listed below.
-"Cookies" are data that may be sent to your web browser and stored on your computer. Most web browsers can be configured not to accept cookies, or to notify you if a cookie is sent to you. If you wish not to have cookies set during your visit to this MITRE managed Site, you can disable them in your web browser.
+- [Personal Information We
+ Collect](#personal-information-we-collect)
-The MITRE ATT&CK website is hosted on GitHub®. MITRE and GitHub use a free third-party software service called Google Analytics® to capture and analyze non-personally identifiable website usage information.
+- [How We Use Personal
+ Information](#how-we-use-personal-information)
-MITRE logs all accesses and the following information is recorded for each Site user: IP address, date and time of access, the requested URL, the referring URL (if provided by the Web browser), and the browser type (if provided by the Web browser). The IP address and its associated domain name (if any) are used to determine broad demographic information. The IP address may be used to track how users navigate through the Site.
+- [How We Share Personal
+ Information](#how-we-share-personal-information)
-MITRE additionally collects User-entered keyword search strings to gauge User interest in specific types of vulnerability information. Each access of an individual ATT&CK entry or candidate page is used to gauge User interest. Web log information may be provided to limited research groups within MITRE to support research related to the World Wide Web. This information does not identify you personally. MITRE may store such information, or it may be included in databases owned and maintained by our service providers.
+- [Linked Websites](#linked-websites)
-For specific information about GitHub's collection and use of information collected from Cookies on web traffic reporting and content tools, please review GitHub's Terms of Use and Privacy Policy at [https://docs.github.com/en/github/site-policy](https://docs.github.com/en/github/site-policy).
+- [Security of Personal
+ Information](#security-of-personal-information)
-## Information Collected from User Subscriptions, E-mail, and Web Forms
+- [Information For Visitors from Outside the United
+ States](#information-for-visitors-from-outside-the-united-states)
-Information that User subscribers may provide, such as company name, location, or job function, is used to determine broad demographic and non-personally identifiable information regarding the types of users of these mailing lists. User subscribers are not required to provide this information. MITRE may provide broad non-personally identifiable demographic information to other organizations.
+- [Information for Visitors from
+ Australia](#information-for-visitors-from-australia)
-ATT&CK-related mailing lists that are sponsored by MITRE are configured to prevent attackers from identifying the subscribers to such mailing lists.
+- [Information for Visitors from the European Economic Area and the
+ United
+ Kingdom](#information-for-visitors-from-the-european-economic-area-and-the-united-kingdom)
-Any User personal information provided to MITRE for inclusion on subscription mailing lists for ATT&CK evaluations will be governed under the MITRE Engenuity® privacy policy, [https://mitre-engenuity.org/privacy](https://mitre-engenuity.org/privacy).
+- [Information for Specific
+ Individuals](#information-for-specific-individuals)
-Users may contact MITRE electronically, via the [Contact Us](https://www.mitre.org/contact-us) page. MITRE may share the information that you provide to us via e-mail within the corporation to respond to your queries, but we do not provide information to anyone outside of the corporation unless required by law to do so.
+- [Privacy of Children](#privacy-of-children)
-## Due Diligence for Intrusion Detection, Prevention, and Reporting
+- [Changes to Our Privacy
+ Policy](#changes-to-our-privacy-policy)
-MITRE performs due diligence to preserve the integrity of the information on the Site. MITRE uses various logging and tracking mechanisms to support the detection, reporting, or recovery from attempted intrusions into attack.mitre.org. MITRE reserves the right to use all available technologies without notice to protect its networks from unauthorized use, and to report attempted intrusions to the appropriate authorities.
+- [Questions](#questions)
-## Information Collected from Third Party Software and Media Sites
+
+## Personal Information We Collect
-When Users visit the Site, Users may link to third party software and/or media sites when they link to another party's website. MITRE does not collect any information that may be collected by that third party; however, information you supply to that third-party software may be collected and/or used by that party. For information about that third party's privacy policy, please see their respective website.
+### Personal Information You Give Us
+MITRE may obtain your personal information when you interact with our
+Site, for example, when you request information using the "**Contact
+Us**" link. Personal information is data that identifies you, or could
+reasonably be used to identify you, as an individual, such as your name
+and email address.
+
+### Information We Collect Automatically
+
+We also may collect other information about your visits to our Site
+using automated tools; for example, cookies and other passive
+information collection technologies enable MITRE to compile aggregate
+statistics concerning use of the Site, analyze trends, enhance the
+security of the Site, deliver content, and otherwise administer
+and improve the Site. This information may include your browser type,
+language preference, operating system, device identifier, device type,
+access time, Internet Protocol (IP) address, the URLs of websites you
+visited before and after visiting our Site, the web search that landed
+you on our Site, length of your visits to our Site, and the links you
+click and pages you visit within our Site.
+
+Your web browser may have settings that allow you to transmit a "**Do
+Not Track**" signal when you visit various websites or use online
+services. Like many websites, our Site is not designed to respond to
+"**Do Not Track**" signals received from browsers. To learn more about
+online tracking, the Federal Trade Commission (FTC) provides guidance on
+[How To Protect Your Privacy
+Online](https://www.consumer.ftc.gov/articles/how-protect-your-privacy-online).
+
+The ATT&CK website is hosted on GitHub®, which provides internet hosting
+for software development and version control. When GitHub is visited,
+the visitor\'s IP address is logged and stored for security purposes,
+regardless of whether the visitor has signed into GitHub or not. For
+more information about GitHub\'s privacy and security practices, see the
+[GitHub Privacy
+Statement](https://docs.github.com/en/github/site-policy/github-privacy-statement).
+MITRE and GitHub use a free third-party software service called Google
+Analytics® to help us understand and analyze how visitors use our Site.
+For more information on how Google Analytics uses data collected through
+the Site, visit . To
+opt out of Google Analytics cookies, visit:
+ and
+.
+
+
+## How We Use Personal Information
+
+MITRE may use personal information we collect through our Site to:
+
+- communicate with you, including to respond to your questions and
+ requests, send you notices about our services, or contact you for
+ additional information when needed;
+
+- analyze Site trends, usage, and the activities of Site visitors;
+
+- improve our Site and notify you about important updates;
+
+- perform internal business analyses or for other business purposes
+ consistent with our mission;
+
+- facilitate, manage, personalize, and improve our partnership
+ relationships;
+
+- identify, prevent, investigate, and take other actions with respect
+ to suspected or actual fraud or illegal activity or other activity
+ that violates our policies;
+
+- ensure the security and integrity of our personal information
+ processing;
+
+- comply with applicable laws, rules, regulations, and legal processes
+ as well as our company policies; and
+
+- fulfill other purposes, with your consent (as required).
+
+
+## How We Share Personal Information
+
+MITRE may share your personal information within our organization to:
+
+- better respond to your inquiries;
+
+- perform marketing research and for sales, support, and
+ service-related purposes;
+
+- protect rights, property, life, health, security, and safety;
+
+- negotiate or complete any proposed or actual merger, purchase, sale,
+ or any other type of acquisition or other transaction, including a
+ transfer of all or a portion of our business to another
+ organization;
+
+- disclose personal information with your consent or at your
+ direction; and
+
+- achieve any other purpose consistent with our statements in this
+ Privacy Policy or otherwise allowed by applicable law.
+
+MITRE may disclose your personal information to comply with applicable
+law, such as in response to requests from law enforcement agencies,
+regulators, other public authorities, courts, and third-party litigants
+in connection with legal proceedings or investigations.
+
+
+## Linked Websites
+
+Our Site may include links to other websites that are not owned or
+operated by MITRE. This Privacy Policy does not apply to those websites,
+which may have their own privacy policies that you should review to
+understand how they may collect, use, or disclose your personal
+information. MITRE is not responsible for the content or privacy
+practices of any linked websites that it does not control.
+
+
+## Security of Personal Information
+
+MITRE maintains reasonable safeguards designed to protect personal
+information from loss, theft, misuse, and unauthorized access,
+disclosure, alteration, and destruction. MITRE employs encryption
+technologies and user authentication procedures that are designed to
+keep data secure. Nevertheless, transmission via the Internet and online
+digital storage are not completely secure, so we cannot guarantee the
+security of your personal information.
+
+
+## Information for Visitors from Outside the United States
+
+MITRE is based in the United States. If you are visiting our Site from
+outside the United States, please be aware that information we obtain
+about you may be transferred to and processed in the United States or
+other jurisdictions. By using the Site and providing your personal
+information, you acknowledge that your personal information may be
+transferred to and processed in jurisdictions outside your own. Please
+be aware that the data protection laws and regulations that may apply to
+your personal information transferred to the United States or other
+countries may be different from the laws in your country of residence.
+
+
+## Information for Visitors from Australia
+
+We are committed to handling your personal information in an open and
+transparent manner in accordance with applicable laws and regulations.
+For more information on your privacy rights, you can visit the website
+of The Office of the Australian Information Commissioner at
+[www.oaic.gov.au/](http://www.oaic.gov.au/).
+
+
+## Information for Visitors from the European Economic Area and the United Kingdom
+
+This section provides a GDPR Notice ("**Notice**") for residents of the
+European Economic Area ("**EEA**") and United Kingdom ("**UK**")
+regarding their respective rights under the European Union's General
+Data Protection Regulation and the United Kingdom's General Data
+Protection Regulation (collectively, the "**GDPR**"). MITRE is the data
+controller for personal data collected through the Site.
+
+This Notice supplements the information in this Privacy Policy and other
+MITRE privacy policies and notices. If there is a conflict between any
+other MITRE privacy policy, statement, or notice and this Notice, this
+Notice will prevail.
+
+### Our Collection and Use of Personal Data
+
+Personal data collected through this Site may include:
+
+- **Contact Data**. You may provide your contact details, such as your
+ name, phone number, postal address, email address, and company
+ affiliation; for example, when you contact us for further
+ information or subscribe to receive our news and information
+ offerings.
+
+- **Device Data**. We may obtain information about devices that access
+ our Site, including the type of device, operating system, device
+ settings, unique device identifiers, and error data.
+
+- **Other Data You Provide**. This includes personal data you include
+ in communications you send to us, such as inquiries about our
+ services.
+
+### Our Processing of Your Personal Data
+
+Your personal data is required for us to provide some of our services.
+In some instances, if you fail to provide your personal data, you may
+not be able to access or use our services. We may process the personal
+data you provide for any of the purposes identified in the "**How We Use
+Personal Information**" and "**How We Share Personal Information**"
+Sections of this Privacy Policy.
+
+Your personal data is processed pursuant to the following **legal
+bases**:
+
+- The processing is **necessary for us to provide you with the
+ services** you request or to respond to your questions.
+
+- We have a **legal obligation** to process your personal data, such
+ as compliance with applicable tax laws or other government
+ regulations or compliance with a court order or binding law
+ enforcement request.
+
+- We have a **legitimate interest** in processing your personal data
+ and our reasons for using the personal data outweigh the potential
+ prejudice to your data protection rights. In particular, we have a
+ legitimate interest in the following instances:
+
+ - To analyze and improve the safety and security of our Site and
+ services, including by implementing and enhancing security
+ measures and safeguards and protecting against fraud, spam, and
+ other abuses;
+
+ - To maintain and improve our Site and services; and
+
+ - To operate and promote MITRE's services and provide you with
+ information and communications about our services that are
+ tailored to, and in accordance with, your preferences.
+
+- You have **consented to** our processing of your personal data. When
+ you consent, you may change your mind and withdraw your consent at
+ any time by emailing us at
+ [privacy@mitre.org](mailto:Privacy@mitre..org).
+
+### Your Rights Under the GDPR
+
+The GDPR provides individuals with certain rights regarding their
+personal data. You may ask us to take the following actions:
+
+- provide you with information about our processing of your personal
+ data and access to your personal data;
+
+- update or correct inaccuracies in your personal data;
+
+- delete your personal data;
+
+- transfer a copy of your personal data to you or a third party of
+ your choice;
+
+- restrict the processing of your personal data;
+
+- object to our use of your personal data for marketing purposes; and
+
+- object to our reliance on legitimate interests as the basis for
+ processing your personal data.
+
+You may submit these requests by email to
+[privacy@mitre.org](mailto:Privacy@MITRE.org). We may
+require specific information from you to help us verify your identity
+prior to processing your request. Applicable law may require or permit
+us to decline your request. If we decline your request, we will tell you
+why, subject to any legal restrictions on disclosing this information.
+
+If you would like to submit a complaint about our use of your personal
+data or our response to your request regarding your personal data, you
+may contact us at
+[privacy@mitre.org](mailto:privacy@mitre.org) or submit a
+complaint directly to the data protection authority in your
+jurisdiction. If you reside in the EEA, you can find information about
+your data protection authority
+[here](https://edpb.europa.eu/about-edpb/about-edpb/members_en).
+If you reside in the UK, you may file complaints with the Information
+Commissioner's Office
+[here](https://ico.org.uk/make-a-complaint/).
+
+### Our Retention of Your Personal Data
+
+MITRE retains your personal data for no longer than is necessary to
+achieve the purposes for which the personal data was collected, or as
+may otherwise be permitted or required under applicable law. To
+determine the appropriate retention period, we will consider the scope
+and sensitivity of the personal data; the potential risk of harm from
+unauthorized access to, use, or disclosure of the data; the purposes for
+which we process the data; whether we can achieve our purposes through
+other means; our business needs; and applicable legal requirements.
+Unless otherwise required by applicable law, at the end of the retention
+period, we will anonymize or securely destroy your personal data.
+
+### Personal Data Transfers
+
+By using this Site, you acknowledge that your personal data may be
+collected, transferred to, and processed in jurisdictions outside your
+own. When you directly provide your personal data through our Site, you
+acknowledge that your personal data is being provided by you to a
+company based in the United States. The laws that apply to personal data
+protection in the United States differ from those applicable in the EEA
+and the UK.
+
+If it is necessary for us to transfer personal data out of the EEA and
+the UK, we do so by using suitable data transfer mechanisms, such as the
+standard contractual clauses approved by the European Commission, which
+impose data protection obligations on parties to the transfer.
+
+
+## Information for Specific Individuals
+
+Residents of U.S. states with consumer privacy laws in effect and
+enforceable may contact us at
+[privacy@mitre.org](mailto:privacy@mitre.org) for further
+information about our privacy practices.
+
+
+## Privacy of Children
+
+This Site is not intended for children, and we do not knowingly collect
+personal information from children. If we become aware that we have
+collected personal information from a child, we will delete it in
+accordance with applicable law.
+
+
## Changes to Our Privacy Policy
-The Site may change from time to time. As a result, at times it may be necessary for us to make changes to this Privacy Policy. Accordingly, MITRE reserves the right to update or modify this Privacy Policy at any time and from time to time without prior notice. Please review this policy periodically, and especially before you provide any information. Your continued use of the Site after any changes or revisions to this Privacy Policy shall indicate your agreement with the terms of such revised Privacy Policy.
+MITRE may update or modify this Privacy Policy from time to time at our
+discretion. We will indicate changes to this Privacy Policy by updating
+the "**Effective Date**" at the beginning of the Privacy Policy. Please
+review this Privacy Policy periodically and especially before you
+provide any personal information to us. Your continued use of this Site
+after any update to this Privacy Policy will constitute your acceptance
+of our changes.
+
+
+## Questions
+
+If you have questions about this Privacy Policy or MITRE's privacy
+practices, you may email
+[privacy@mitre.org](mailto:privacy@mitre.org).
+
+MITRE's Data Protection Officer for Singapore may be contacted as
+follows:
+
+**In the United States**
+Dena Kozanas -- *Data Protection Officer *
+Associate General Counsel & Chief Privacy Official
+7515 Colshire Drive
+McLean, VA 22102
+Phone: +1 (703) 269-8515
+Email: [privacy@mitre.org](mailto:%20privacy@mitre.org)
+
+**In Singapore **
+MITRE Asia Pacific Singapore
+Thomas (Tass) Bruce Hudak -- *Privacy Coordinator *
+1 Changi Business Park Avenue 1
+Suite #02-03/04
+Singapore 486058
+Phone: +65 8876 4609
+Email: [privacy@mitre.org](mailto:%20privacy@mitre.org)
diff --git a/modules/site_config.py b/modules/site_config.py
index 9bb53627140..69167a5df16 100644
--- a/modules/site_config.py
+++ b/modules/site_config.py
@@ -135,17 +135,9 @@ def set_subdirectory(subdirectory_str):
# Redirect md string template
redirect_md_index = Template(
- "Title: ${title}\n"
- "Template: general/redirect-index\n"
- "RedirectLink: ${to}\n"
- "save_as: ${from}/index.html"
-)
-redirect_md = Template(
- "Title: ${title}\n"
- "Template: general/redirect-index\n"
- "RedirectLink: ${to}\n"
- "save_as: ${from}"
+ "Title: ${title}\nTemplate: general/redirect-index\nRedirectLink: ${to}\nsave_as: ${from}/index.html"
)
+redirect_md = Template("Title: ${title}\nTemplate: general/redirect-index\nRedirectLink: ${to}\nsave_as: ${from}")
# Custom_alphabet used to sort list of dictionaries by domain name
# depending on domain ordering
@@ -174,10 +166,7 @@ def set_subdirectory(subdirectory_str):
# domain: "enterprise", "mobile", "ics"
# path: the path to the object, e.g "software/S1001" or "groups/G2021"
layer_md = Template(
- "Title: ${domain} Techniques\n"
- "Template: general/json\n"
- "save_as: ${path}/${attack_id}-${domain}-layer.json\n"
- "json: "
+ "Title: ${domain} Techniques\nTemplate: general/json\nsave_as: ${path}/${attack_id}-${domain}-layer.json\njson: "
)
layer_version = "4.5"
navigator_version = "5.1.0"
@@ -191,3 +180,5 @@ def set_subdirectory(subdirectory_str):
GOOGLE_ANALYTICS = os.getenv("GOOGLE_ANALYTICS")
GOOGLE_SITE_VERIFICATION = os.getenv("GOOGLE_SITE_VERIFICATION")
+
+INCLUDE_OSANO = os.getenv("INCLUDE_OSANO")
diff --git a/modules/software/__init__.py b/modules/software/__init__.py
index 103553ec56d..e65709aeede 100644
--- a/modules/software/__init__.py
+++ b/modules/software/__init__.py
@@ -2,14 +2,17 @@
from . import software_config
import json
+
def get_priority():
return software_config.priority
+
# TODO commented out to resolve infinite redirect loop when run locally. Needs further testing before code removal.
# def get_redirections():
# with open(software_config.software_redirection_location , "r", encoding="utf8") as json_redirections:
# return json.load(json_redirections)
# return []
+
def run_module():
return software.generate_software(), software_config.module_name
diff --git a/modules/software/software_config.py b/modules/software/software_config.py
index 304e3ca3a6f..e399491f585 100644
--- a/modules/software/software_config.py
+++ b/modules/software/software_config.py
@@ -10,16 +10,10 @@
software_templates_path = "modules/software/templates/"
# String template for software index page
-software_index_md = ("Title: Software overview\n"
- "Template: software/software-index\n"
- "save_as: software/index.html\n"
- "data: ")
+software_index_md = "Title: Software overview\nTemplate: software/software-index\nsave_as: software/index.html\ndata: "
# String template for group page
-software_md = Template("Title: ${name}\n"
- "Template: software/software\n"
- "save_as: software/${attack_id}/index.html\n"
- "data: ")
+software_md = Template("Title: ${name}\nTemplate: software/software\nsave_as: software/${attack_id}/index.html\ndata: ")
software_redirection_location = "modules/software/software_redirections.json"
@@ -28,4 +22,4 @@
"Template: general/sidebar-template \n"
"save_as: software/sidebar-software/index.html\n"
"data: "
-)
\ No newline at end of file
+)
diff --git a/modules/stixtests/linkbyidchecker.py b/modules/stixtests/linkbyidchecker.py
index 04de5b8e15d..d66268fbb01 100644
--- a/modules/stixtests/linkbyidchecker.py
+++ b/modules/stixtests/linkbyidchecker.py
@@ -126,7 +126,7 @@ def linkbyid_check():
"Object with broken data": pretty_name,
"Section found in": "Description",
# sort of abusing "Unknown LinkById" here, but it works well enough
- "Unknown LinkById": f"{{{{LinkById\|{attack_id_in_description}}}}}",
+ "Unknown LinkById": rf"{{{{LinkById|{attack_id_in_description}}}}}",
}
link_by_id_warnings.append(warning)
diff --git a/modules/subdirectory/subdirectory.py b/modules/subdirectory/subdirectory.py
index 0b42bcb7101..ad9c67d7d28 100644
--- a/modules/subdirectory/subdirectory.py
+++ b/modules/subdirectory/subdirectory.py
@@ -11,27 +11,7 @@ def generate_subdirectory():
replace()
-allowed_in_link = "".join(
- list(
- map(
- lambda s: s.strip(),
- [
- " - ",
- " ? ",
- " \w ",
- " \\ ",
- " $ ",
- " \. ",
- " ! ",
- " \* ",
- " ' ",
- " () ",
- " / ",
- ],
- )
- )
-)
-
+allowed_in_link = r"-?\w\$\.!\*'()/"
def replace_links(filepath):
"""In the given file, replace the in-site links to reference
@@ -43,7 +23,7 @@ def replace_links(filepath):
html_str = html.read()
# subdirectory link format
- dest_link_format = f"/{site_config.subdirectory}\g<1>"
+ dest_link_format = rf"/{site_config.subdirectory}\g<1>"
def substitute(prefix, html_str):
from_str = f"{prefix}=[\"']([{allowed_in_link}]+)[\"']"
diff --git a/modules/tactics/__init__.py b/modules/tactics/__init__.py
index 611812692c2..015f14636a8 100644
--- a/modules/tactics/__init__.py
+++ b/modules/tactics/__init__.py
@@ -21,6 +21,7 @@ def get_menu():
],
}
+
# TODO resolve infinite redirect loop when run locally. Needs further testing before code removal.
def get_redirections():
with open(tactics_config.tactics_redirection_location, "r", encoding="utf8") as json_redirections:
diff --git a/modules/tactics/tactics.py b/modules/tactics/tactics.py
index 2008ddbd57b..2037c9bfc6d 100644
--- a/modules/tactics/tactics.py
+++ b/modules/tactics/tactics.py
@@ -190,6 +190,7 @@ def get_techniques_of_tactic(tactic, techniques):
techniques_list = sorted(techniques_list, key=lambda k: k["name"].lower())
return techniques_list
+
def generate_sidebar_tactics(side_nav_data):
"""Responsible for generating the sidebar for the tactics pages."""
logger.info("Generating tactics sidebar")
@@ -200,6 +201,7 @@ def generate_sidebar_tactics(side_nav_data):
sidebar_tactics_md = tactics_config.sidebar_tactics_md + json.dumps(data)
# write markdown to file
- with open(os.path.join(tactics_config.tactics_markdown_path, "sidebar_tactics.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(tactics_config.tactics_markdown_path, "sidebar_tactics.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(sidebar_tactics_md)
-
diff --git a/modules/tactics/tactics_config.py b/modules/tactics/tactics_config.py
index b8dd5e4fa71..99d3dd261c1 100644
--- a/modules/tactics/tactics_config.py
+++ b/modules/tactics/tactics_config.py
@@ -11,12 +11,12 @@
# String template for domains
tactic_domain_md = Template(
- "Title: Tactics\n" "Template: tactics/tactics-domain-index\n" "save_as: tactics/${domain}/index.html\n" "data: "
+ "Title: Tactics\nTemplate: tactics/tactics-domain-index\nsave_as: tactics/${domain}/index.html\ndata: "
)
# String template for tactics
tactic_md = Template(
- "Title: ${name}-${domain}\n" "Template: tactics/tactic\n" "save_as: tactics/${attack_id}/index.html\n" "data: "
+ "Title: ${name}-${domain}\nTemplate: tactics/tactic\nsave_as: tactics/${attack_id}/index.html\ndata: "
)
# Tactics overview md template
@@ -30,8 +30,5 @@
tactics_redirection_location = "modules/tactics/tactics_redirections.json"
sidebar_tactics_md = (
- "Title: Tactics Sidebar\n"
- "Template: general/sidebar-template \n"
- "save_as: tactics/sidebar-tactics/index.html\n"
- "data: "
-)
\ No newline at end of file
+ "Title: Tactics Sidebar\nTemplate: general/sidebar-template \nsave_as: tactics/sidebar-tactics/index.html\ndata: "
+)
diff --git a/modules/techniques/techniques.py b/modules/techniques/techniques.py
index 3d88ed39f3f..3f3fb7918be 100644
--- a/modules/techniques/techniques.py
+++ b/modules/techniques/techniques.py
@@ -259,7 +259,7 @@ def generate_data_for_md(technique_dict, technique, tactic_list, is_sub_techniqu
if technique.get("x_mitre_system_requirements"):
technique["x_mitre_system_requirements"].sort()
technique_dict["sysreqs"] = ", ".join(technique["x_mitre_system_requirements"])
- technique_dict["sysreqs"] = re.sub("\.?\\n+", "; ", technique_dict["sysreqs"])
+ technique_dict["sysreqs"] = re.sub(r"\.?\\n+", "; ", technique_dict["sysreqs"])
# Get permissions required
if technique.get("x_mitre_permissions_required"):
@@ -361,15 +361,14 @@ def get_mitigations_table_data(technique, reference_list):
if not mitigation["object"].get("x_mitre_deprecated"):
attack_id = util.buildhelpers.get_attack_id(mitigation["object"])
# Only add if mitigation attack id is found
- if not attack_id: continue
+ if not attack_id:
+ continue
row = {}
row["mid"] = attack_id
row["name"] = mitigation["object"]["name"]
if mitigation["relationship"].get("description"):
# Get filtered description
- reference_list = util.buildhelpers.update_reference_list(
- reference_list, mitigation["relationship"]
- )
+ reference_list = util.buildhelpers.update_reference_list(reference_list, mitigation["relationship"])
row["descr"] = mitigation["relationship"]["description"]
mitigation_data.append(row)
@@ -396,15 +395,14 @@ def get_assets_table_data(technique, reference_list):
attack_id = util.buildhelpers.get_attack_id(asset["object"])
# Only add if attack id is found
- if not attack_id: continue
+ if not attack_id:
+ continue
row = {}
row["id"] = attack_id
row["name"] = asset["object"]["name"]
if asset["relationship"].get("description"):
# Get filtered description
- reference_list = util.buildhelpers.update_reference_list(
- reference_list, asset["relationship"]
- )
+ reference_list = util.buildhelpers.update_reference_list(reference_list, asset["relationship"])
row["descr"] = asset["relationship"]["description"]
asset_data.append(row)
@@ -436,7 +434,8 @@ def get_examples_table_data(technique, reference_list):
attack_id = util.buildhelpers.get_attack_id(example["object"])
# Only add example data if the attack id is found
- if not attack_id: continue
+ if not attack_id:
+ continue
row = {}
row["id"] = attack_id
@@ -447,9 +446,7 @@ def get_examples_table_data(technique, reference_list):
if example["relationship"].get("description"):
# Get filtered description
- reference_list = util.buildhelpers.update_reference_list(
- reference_list, example["relationship"]
- )
+ reference_list = util.buildhelpers.update_reference_list(reference_list, example["relationship"])
row["descr"] = example["relationship"]["description"]
example_data.append(row)
@@ -544,7 +541,8 @@ def get_techniques_list(techniques):
if not technique.get("revoked") and not technique.get("x_mitre_deprecated"):
attack_id = util.buildhelpers.get_attack_id(technique)
- if not attack_id: continue
+ if not attack_id:
+ continue
technique_dict = {}
technique_dict["id"] = attack_id
@@ -649,6 +647,7 @@ def get_datasources_and_components_of_technique(technique, reference_list):
return datasource_and_components, show_descriptions
+
def generate_sidebar_techniques(side_nav_data):
"""Responsible for generating the sidebar for the technique pages."""
logger.info("Generating technique sidebar")
@@ -659,5 +658,7 @@ def generate_sidebar_techniques(side_nav_data):
sidebar_techniques_md = techniques_config.sidebar_techniques_md + json.dumps(data)
# write markdown to file
- with open(os.path.join(techniques_config.techniques_markdown_path, "sidebar_techniques.md"), "w", encoding="utf8") as md_file:
+ with open(
+ os.path.join(techniques_config.techniques_markdown_path, "sidebar_techniques.md"), "w", encoding="utf8"
+ ) as md_file:
md_file.write(sidebar_techniques_md)
diff --git a/modules/techniques/techniques_config.py b/modules/techniques/techniques_config.py
index 1679960fb7d..399c4f58fbd 100644
--- a/modules/techniques/techniques_config.py
+++ b/modules/techniques/techniques_config.py
@@ -11,18 +11,12 @@
# String template for all techniques
technique_md = Template(
- "Title: ${name}-${domain}\n"
- "Template: techniques/technique\n"
- "save_as: techniques/${attack_id}/index.html\n"
- "data: "
+ "Title: ${name}-${domain}\nTemplate: techniques/technique\nsave_as: techniques/${attack_id}/index.html\ndata: "
)
# String template for domains
technique_domain_md = Template(
- "Title: Techniques\n"
- "Template: techniques/techniques-domain-index\n"
- "save_as: techniques/${domain}/index.html\n"
- "data: "
+ "Title: Techniques\nTemplate: techniques/techniques-domain-index\nsave_as: techniques/${domain}/index.html\ndata: "
)
# Overview md template
@@ -48,4 +42,4 @@
"Template: general/sidebar-template \n"
"save_as: techniques/sidebar-techniques/index.html\n"
"data: "
-)
\ No newline at end of file
+)
diff --git a/modules/tests/citationchecker.py b/modules/tests/citationchecker.py
index d4870fa55a8..2509b19e7a7 100644
--- a/modules/tests/citationchecker.py
+++ b/modules/tests/citationchecker.py
@@ -7,7 +7,7 @@
from . import tests_config
-potential_issues_list = ["\(Citation: ?[^)]+\)?"]
+potential_issues_list = [r"\(Citation: ?[^)]+\)?"]
def citations_check():
diff --git a/modules/tests/linkchecker.py b/modules/tests/linkchecker.py
index 9cd1f339a31..9e2a7f1f8fb 100644
--- a/modules/tests/linkchecker.py
+++ b/modules/tests/linkchecker.py
@@ -1,61 +1,18 @@
import os
import re
-from pathlib import Path
import requests
-from loguru import logger
-from modules import site_config
import modules
+from modules import site_config
+
from . import tests_config
# STATIC PROPERTIES
-# The spaces here are for readability with symbol characters.
-# The strings are stripped of whitespace.
-# These get compiled into a regex string
-
-allowed_in_link_with_external_links = "".join(
- list(
- map(
- lambda s: s.strip(),
- [
- " - ",
- " ? ",
- " \w ",
- " \\ ",
- " $ ",
- " \. ",
- " ! ",
- " \* ",
- " ' ",
- " () ",
- " / ",
- " : ",
- ],
- )
- )
-)
-
-allowed_in_link = "".join(
- list(
- map(
- lambda s: s.strip(),
- [
- " - ",
- " ? ",
- " \w ",
- " \\ ",
- " $ ",
- " \. ",
- " ! ",
- " \* ",
- " ' ",
- " () ",
- " / ",
- ],
- )
- )
-)
+
+allowed_in_link_with_external_links = r"-?\w\$\.!\*'()/:"
+
+allowed_in_link = r"-?\w\$\.!\*'()/"
links_list = {}
in_use_links = {}
@@ -81,7 +38,7 @@
def remove_extra_from_path(filepath):
- """Given a path, remove unwanted path from a website link"""
+ """Given a path, remove unwanted path from a website link."""
return filepath.split(site_config.parent_web_directory)[1]
@@ -114,7 +71,7 @@ def get_correct_link(path):
"docx",
"rtf",
]:
- if re.search("(css|js)\?[\w\d]+", sort_of_extension):
+ if re.search(r"(css|js)\?\w+", sort_of_extension):
# CSS & JavaScript: check for cache-disabling query string suffix, e.g style.min.css?f8be4c06
path = path.split("?")[0] # remove suffix
else:
@@ -135,7 +92,7 @@ def check_if_link_in_use(filepath, link):
If not, verify that the link is not the same as the filepath and add it to the in use links map.
"""
- if not "previous" in link and not "versions" in link:
+ if "previous" not in link and "versions" not in link:
if not in_use_links.get(link):
new_file_name = remove_extra_from_path(filepath)
@@ -146,19 +103,15 @@ def check_if_link_in_use(filepath, link):
in_use_links[link] = True
-def remove_subdirectory_from_web_directory():
- if site_config.subdirectory:
- return site_config.web_directory.split(site_config.subdirectory)[0]
- else:
- return site_config.web_directory
-
-
def internal_link_test(link):
"""Given a link, make sure that that it exists on the file system."""
+ if site_config.subdirectory:
+ web_dir = site_config.web_directory.split(site_config.subdirectory)[0]
+ else:
+ web_dir = site_config.web_directory
+
# Get correct link path
- path = get_correct_link(link)
-
- path = remove_subdirectory_from_web_directory() + path
+ path = web_dir + get_correct_link(link)
# e.g: contacts.html -> contacts/index.html
to_index_path = path
@@ -201,9 +154,9 @@ def internal_external_link_checker(filepath, html_str):
if (
"/versions/" in filepath
): # don't check links with data-test-ignore attribute, or live version link name, when on previous versions
- linkregex = f'{prefix}\s?=\s?["\']([{allowed_in_link_with_external_links}]+)["\'](?! ?data-test-ignore="true")(?!>live version)'
+ linkregex = rf'{prefix}\s?=\s?["\']([{allowed_in_link_with_external_links}]+)["\'](?! ?data-test-ignore="true")(?!>live version)'
else:
- linkregex = f"{prefix}\s?=\s?[\"']([{allowed_in_link_with_external_links}]+)[\"']"
+ linkregex = rf"{prefix}\s?=\s?[\"']([{allowed_in_link_with_external_links}]+)[\"']"
links = re.findall(linkregex, html_str)
# check if link has a dest
@@ -213,7 +166,7 @@ def internal_external_link_checker(filepath, html_str):
# Add to relative links list if relative
if is_relative:
- if not link in relative_links:
+ if link not in relative_links:
relative_links.append(link)
# Get correct path
@@ -262,7 +215,7 @@ def internal_link_checker(filepath, html_str):
# find all links
for prefix in ["href", "src"]:
- links = re.findall(f"{prefix}\s?=\s?[\"']([{allowed_in_link}]+)[\"']", html_str)
+ links = re.findall(rf"{prefix}\s?=\s?[\"']([{allowed_in_link}]+)[\"']", html_str)
# check if link has a dest
for link in links:
# Check if link is relative path
@@ -270,7 +223,7 @@ def internal_link_checker(filepath, html_str):
# Add to relative links list if relative
if is_relative:
- if not link in relative_links:
+ if link not in relative_links:
relative_links.append(link)
# Get correct path
@@ -316,7 +269,7 @@ def check_unlinked_pages(filenames):
"""
unlinked_pages = []
for filename in filenames:
- if not "previous" in filename and not "versions" in filename:
+ if "previous" not in filename and "versions" not in filename:
# Check if it is deprecated
if check_if_file_is_deprecated(filename):
continue
@@ -365,7 +318,7 @@ def check_links_on_page(filepath, check_external_links=False):
with open(filepath, mode="r", encoding="utf8") as html:
html_str = html.read()
- if not "previous" in filepath and not "versions" in filepath:
+ if "previous" not in filepath and "versions" not in filepath:
# Add redirects to in-use to avoid false positives
if html_str.startswith(' {asset, relationship} for each asset targeted by the technique.
@@ -262,6 +263,7 @@ def assets_targeted_by_techniques(srcs):
"""
return get_related(srcs, "attack-pattern", "targets", "x-mitre-asset")
+
# technique:malware
def techniques_used_by_malware(srcs):
"""Return malware => {technique, relationship} for each technique used by the malware.
diff --git a/modules/versions/versions.py b/modules/versions/versions.py
index c7f965ba4e7..a2baea51ff4 100644
--- a/modules/versions/versions.py
+++ b/modules/versions/versions.py
@@ -14,8 +14,7 @@
def generate_versions():
- """Responsible for generating the versions pages"""
-
+ """Responsible for generating the versions pages."""
# Move templates to templates directory
util.buildhelpers.move_templates(versions_config.module_name, versions_config.versions_templates_path)
@@ -53,30 +52,10 @@ def onerror(func, path, exc_info):
# allowed characters inside of hyperlinks
-allowed_in_link = "".join(
- list(
- map(
- lambda s: s.strip(),
- [
- " - ",
- " ? ",
- " \w ",
- " \\ ",
- " $ ",
- " \. ",
- " ! ",
- " \* ",
- " ' ",
- " () ",
- " / ",
- ],
- )
- )
-)
-
+allowed_in_link = r"-?\w\$\.!\*'()/"
def versionPath(version):
- # get the path of a given version
+ """Get the path of a given version."""
if "path" in version:
return version["path"]
else:
@@ -84,7 +63,7 @@ def versionPath(version):
def deploy():
- """Deploy previous versions to website directory"""
+ """Deploy previous versions to website directory."""
versions_config.prev_versions_deploy_folder = os.path.join(
site_config.web_directory, versions_config.prev_versions_path
)
@@ -176,7 +155,8 @@ def deploy_previous_version(version, repo):
def archive(version_data, is_current=False):
- """perform archival operations on a version in /prev_versions_path
+ """Perform archival operations on a version in /prev_versions_path.
+
- remove unnecessary files (.git, CNAME, preserved versions for that version)
- replace links on all pages
- add archived version banner to all pages
@@ -236,10 +216,10 @@ def saferemove(path, type):
html_str = html.read()
# helper function to substitute links so that they point to /versions/
- dest_link_format = f"/{version_url_path}\g<1>"
+ dest_link_format = rf"/{version_url_path}\g<1>"
def substitute(prefix, html_str):
- fromstr = f"{prefix}=[\"'](?!\/versions\/)([{allowed_in_link}]+)[\"']"
+ fromstr = rf"{prefix}=[\"'](?!\/versions\/)([{allowed_in_link}]+)[\"']"
tostr = f'{prefix}="{dest_link_format}"'
return re.sub(fromstr, tostr, html_str)
@@ -262,8 +242,8 @@ def substitute_redirection(prefix, html_str):
# update versioning button to show the permalink site version, aka "back to main site"
html_str = html_str.replace("version-button live", "version-button permalink")
# update live version links on the versioning button
- from_str = f"href=[\"']\/versions\/v[\w-]+\/([{allowed_in_link}]+)[\"'](.*)>[Ll]ive [Vv]ersion<\/a>"
- to_str = f'href="/\g<1>"\g<2>>Live Version'
+ from_str = rf"href=[\"']\/versions\/v[\w-]+\/([{allowed_in_link}]+)[\"'](.*)>[Ll]ive [Vv]ersion<\/a>"
+ to_str = r'href="/\g<1>"\g<2>>Live Version'
html_str = re.sub(from_str, to_str, html_str)
# remove banner message if it is present
@@ -323,7 +303,8 @@ def substitute_redirection(prefix, html_str):
def build_alias(version, alias):
- """build redirects from alias to version
+ """Build redirects from alias to version.
+
version is the path of the version, e.g "v5"
alias is the alias to build, e.g "october2018"
"""
@@ -349,7 +330,7 @@ def build_alias(version, alias):
def build_markdown(versions):
- """build markdown for the versions list page"""
+ """Build markdown for the versions list page."""
# build urls
versions["current"]["url"] = versionPath(versions["current"])
versions["current"]["changelog_label"] = " ".join(versions["current"]["changelog"].split("-")[1:]).title()
diff --git a/modules/versions/versions_config.py b/modules/versions/versions_config.py
index 0ca96ad20f1..8bc15a181f7 100644
--- a/modules/versions/versions_config.py
+++ b/modules/versions/versions_config.py
@@ -16,6 +16,4 @@
versions_repo = "https://github.com/mitre-attack/attack-website.git"
versions_directory = "attack-versions"
-versions_md = (
- "Title: Version History\n" "Template: versions/versions\n" "save_as: resources/versions/index.html\n" "data: "
-)
+versions_md = "Title: Version History\nTemplate: versions/versions\nsave_as: resources/versions/index.html\ndata: "
diff --git a/modules/website_build/website_build.py b/modules/website_build/website_build.py
index cf58b5299b3..28ab9aeb8e8 100644
--- a/modules/website_build/website_build.py
+++ b/modules/website_build/website_build.py
@@ -236,17 +236,22 @@ def pelican_content():
google_analytics = site_config.GOOGLE_ANALYTICS
google_site_verification = site_config.GOOGLE_SITE_VERIFICATION
+ include_osano = site_config.INCLUDE_OSANO
if site_config.args.google_analytics:
google_analytics = site_config.args.google_analytics
if site_config.args.google_site_verification:
google_site_verification = site_config.args.google_site_verification
+ if site_config.args.include_osano:
+ include_osano = site_config.args.include_osano
extra_settings = ""
if google_analytics:
extra_settings = f"{extra_settings} GOOGLE_ANALYTICS='\"{google_analytics}\"'"
if google_site_verification:
extra_settings = f"{extra_settings} GOOGLE_SITE_VERIFICATION='\"{google_site_verification}\"'"
+ if include_osano:
+ extra_settings = f"{extra_settings} INCLUDE_OSANO='\"{include_osano}\"'"
if extra_settings:
pelican_cmd = f"{pelican_cmd} -e {extra_settings}"
diff --git a/modules/website_build/website_build_config.py b/modules/website_build/website_build_config.py
index 46c9f50636e..b9c76d4a00b 100644
--- a/modules/website_build/website_build_config.py
+++ b/modules/website_build/website_build_config.py
@@ -57,7 +57,7 @@
}
# ATT&CK overview
-attack_index_md = "Title: ATT&CK Overview \n" "Template: general/attack-index \n" "save_as: index.html\n" "data: "
+attack_index_md = "Title: ATT&CK Overview \nTemplate: general/attack-index \nsave_as: index.html\ndata: "
# ATT&CK index markdown path
attack_index_path = "content/pages/index.md"
@@ -71,4 +71,4 @@
website_build_templates_path = "modules/website_build/templates/"
# CHANGELOG md
-changelog_md = "Title: Changelog\n" "Template: website_build/changelog\n" "save_as: resources/changelog.html\n\n"
+changelog_md = "Title: Changelog\nTemplate: website_build/changelog\nsave_as: resources/changelog.html\n\n"
diff --git a/pelicanconf.py b/pelicanconf.py
index 4c74492c07b..320118d5e71 100644
--- a/pelicanconf.py
+++ b/pelicanconf.py
@@ -8,20 +8,20 @@
# read file with custom jinja filters
import sys
-sys.path.append('.')
+sys.path.append(".")
import custom_jinja_filters
-AUTHOR = os.environ.get('PELICAN_AUTHOR', 'MITRE')
-SITENAME = os.environ.get('PELICAN_SITENAME', 'ATT&CK')
-SITEURL = os.environ.get('PELICAN_SITEURL', '')
+AUTHOR = os.environ.get("PELICAN_AUTHOR", "MITRE")
+SITENAME = os.environ.get("PELICAN_SITENAME", "ATT&CK")
+SITEURL = os.environ.get("PELICAN_SITEURL", "")
-PATH = 'content'
+PATH = "content"
-TIMEZONE = os.environ.get('PELICAN_TIMEZONE', 'America/New_York')
+TIMEZONE = os.environ.get("PELICAN_TIMEZONE", "America/New_York")
-DEFAULT_LANG = os.environ.get('PELICAN_DEFAULT_LANG','en')
+DEFAULT_LANG = os.environ.get("PELICAN_DEFAULT_LANG", "en")
-THEME = 'attack-theme'
+THEME = "attack-theme"
# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
@@ -31,18 +31,18 @@
AUTHOR_FEED_RSS = None
DEFAULT_PAGINATION = False
-STATIC_PATHS = ['docs']
-ARTICLE_PATHS = ['pages/updates']
+STATIC_PATHS = ["docs"]
+ARTICLE_PATHS = ["pages/updates"]
# Uncomment following line if you want document-relative URLs when developing
RELATIVE_URLS = False
JINJA_FILTERS = {
- 'from_json': custom_jinja_filters.json.loads,
- 'flatten_tree': custom_jinja_filters.flatten_tree,
- 'clean_path': custom_jinja_filters.clean_path,
- 'remove_whitespace': custom_jinja_filters.remove_whitespace,
- 'escape_spaces': custom_jinja_filters.escape_spaces,
- 'stixToHTML': custom_jinja_filters.stixToHTML,
- 'permalink': custom_jinja_filters.permalink
+ "from_json": custom_jinja_filters.json.loads,
+ "flatten_tree": custom_jinja_filters.flatten_tree,
+ "clean_path": custom_jinja_filters.clean_path,
+ "remove_whitespace": custom_jinja_filters.remove_whitespace,
+ "escape_spaces": custom_jinja_filters.escape_spaces,
+ "stixToHTML": custom_jinja_filters.stixToHTML,
+ "permalink": custom_jinja_filters.permalink,
}
diff --git a/update-attack.py b/update-attack.py
index 4b4fc048bdb..a712b787d0f 100644
--- a/update-attack.py
+++ b/update-attack.py
@@ -158,6 +158,11 @@ def get_parsed_args():
type=str,
help=("If a Google site verification code is provided, then the site will include it on all pages."),
)
+ parser.add_argument(
+ "--include-osano",
+ action="store_true",
+ help=("If specified, the site will include the Osano privacy compliance script."),
+ )
args = parser.parse_args()
@@ -230,7 +235,7 @@ def remove_from_menu():
ptr["run_module"]()
end_time = time.time()
util.buildhelpers.print_end(ptr["module_name"], start_time, end_time)
-
+
# Print end of module
update_end = time.time()
util.buildhelpers.print_end("TOTAL Update Time", update_start, update_end)